Compare commits
12 Commits
455f6deec3
...
7121e502df
Author | SHA1 | Date | |
---|---|---|---|
7121e502df | |||
4a2d646dc0 | |||
f9d7af1744 | |||
bc3a2ddf99 | |||
7ac20b1841 | |||
2362ce6cd4 | |||
2d00b675bf | |||
21aa6826c7 | |||
189229ca7a | |||
a8b7e88cfe | |||
45055998bd | |||
70490c9aa8 |
135
Cargo.lock
generated
135
Cargo.lock
generated
@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@ -17,10 +23,38 @@ 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 = "error"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[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",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"rustc_version",
|
||||
"static_assertions",
|
||||
"unsafe_unwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
@ -28,15 +62,116 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"error",
|
||||
"fdt-rs",
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
|
||||
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.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
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 = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||
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"
|
||||
|
29
Makefile
29
Makefile
@ -25,8 +25,8 @@ ifeq ($(ARCH),x86_64)
|
||||
$(error TODO)
|
||||
else
|
||||
ifeq ($(MACH),qemu)
|
||||
QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
-M virt,virtualization=off \
|
||||
QEMU_OPTS+=-kernel $(O)/kernel \
|
||||
-M virt,virtualization=on \
|
||||
-cpu cortex-a72 \
|
||||
-m 512 \
|
||||
-serial chardev:serial0 \
|
||||
@ -40,18 +40,33 @@ QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
endif
|
||||
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
|
||||
|
||||
kernel:
|
||||
cd kernel && cargo build $(CARGO_BUILD_OPTS)
|
||||
ifeq ($(ARCH),aarch64)
|
||||
$(OBJCOPY) -O binary $(O)/kernel $(O)/kernel.bin
|
||||
endif
|
||||
ifeq ($(MACH),orangepi3)
|
||||
$(LLVM_BASE)/llvm-strip $(O)/kernel
|
||||
$(LLVM_BASE)/llvm-size $(O)/kernel
|
||||
$(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
|
||||
$(MKIMAGE) \
|
||||
-A arm64 \
|
||||
-O linux \
|
||||
-T kernel \
|
||||
-C none \
|
||||
-a 0x48000000 \
|
||||
-e 0x48000000 \
|
||||
-n kernel \
|
||||
-d $(O)/kernel.bin \
|
||||
$(O)/uImage
|
||||
endif
|
||||
|
||||
clean:
|
||||
|
9
address/Cargo.toml
Normal file
9
address/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "address"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
error = { path = "../error" }
|
0
address/src/base/mod.rs
Normal file
0
address/src/base/mod.rs
Normal file
16
address/src/lib.rs
Normal file
16
address/src/lib.rs
Normal file
@ -0,0 +1,16 @@
|
||||
//! Type-safe wrappers for different address kinds
|
||||
#![no_std]
|
||||
#![feature(step_trait, const_fn_trait_bound, const_trait_impl, const_panic)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[deny(missing_docs)]
|
||||
pub mod phys;
|
||||
pub mod virt;
|
||||
|
||||
trait Address {}
|
||||
|
||||
pub use phys::PhysicalAddress;
|
||||
pub use virt::{AddressSpace, NoTrivialConvert, TrivialConvert, VirtualAddress};
|
183
address/src/phys.rs
Normal file
183
address/src/phys.rs
Normal file
@ -0,0 +1,183 @@
|
||||
//! Module for handling physical memory addresses
|
||||
|
||||
use crate::{AddressSpace, TrivialConvert, VirtualAddress};
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::iter::Step;
|
||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
|
||||
/// Wrapper struct for representing a physical address
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct PhysicalAddress(usize);
|
||||
|
||||
// Arithmetic
|
||||
impl const Add<usize> for PhysicalAddress {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(self, rhs: usize) -> Self {
|
||||
// Will panic on overflow
|
||||
Self::from(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
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 const 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 const From<usize> for PhysicalAddress {
|
||||
fn from(p: usize) -> Self {
|
||||
Self(p)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl const From<u64> for PhysicalAddress {
|
||||
fn from(p: u64) -> Self {
|
||||
Self(p as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalAddress {
|
||||
/// Computes a signed difference between `start` and `end`
|
||||
/// addresses. Will panic if the result cannot be represented due to
|
||||
/// overflow.
|
||||
#[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")
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes a signed difference between `start` and `end`, does not check for
|
||||
/// overflow condition.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not check for signed integer overflow.
|
||||
#[inline(always)]
|
||||
pub unsafe fn diff_unchecked(start: PhysicalAddress, end: PhysicalAddress) -> isize {
|
||||
end.0 as isize - start.0 as isize
|
||||
}
|
||||
|
||||
/// Returns `true` if the address is page-aligned
|
||||
#[inline(always)]
|
||||
pub const fn is_paligned(self) -> bool {
|
||||
self.0 & 0xFFF == 0
|
||||
}
|
||||
|
||||
/// Returns the index of the 4K page containing the address
|
||||
#[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 const 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);
|
||||
}
|
||||
}
|
361
address/src/virt.rs
Normal file
361
address/src/virt.rs
Normal file
@ -0,0 +1,361 @@
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,35 +1,44 @@
|
||||
ENTRY(_entry);
|
||||
|
||||
MEMORY {
|
||||
ram : ORIGIN = 0x48000000, LENGTH = 992M
|
||||
}
|
||||
KERNEL_OFFSET = 0xFFFFFF8000000000;
|
||||
BASE_OFFSET = 0x48000000;
|
||||
|
||||
SECTIONS {
|
||||
. = BASE_OFFSET;
|
||||
|
||||
.text.lower : {
|
||||
*(.text._entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_OFFSET;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : {
|
||||
*(.text._entry)
|
||||
.text : AT(. - KERNEL_OFFSET) {
|
||||
*(.text._entry_upper)
|
||||
*(.text*)
|
||||
} >ram
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : {
|
||||
.rodata : AT(. - KERNEL_OFFSET) {
|
||||
*(.rodata*)
|
||||
} >ram
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : {
|
||||
.data : AT(. - KERNEL_OFFSET) {
|
||||
*(.data*)
|
||||
} >ram
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.bss : {
|
||||
PROVIDE(__bss_start = .);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_OFFSET);
|
||||
PROVIDE(__bss_start = .);
|
||||
.bss : AT(. - KERNEL_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end = .);
|
||||
} >ram
|
||||
}
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_OFFSET);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"max-atomic-width": 128,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"relocation-model": "pic",
|
||||
"target-pointer-width": "64",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [ "-Tetc/aarch64-qemu.ld" ]
|
||||
|
@ -1,14 +1,22 @@
|
||||
ENTRY(_entry);
|
||||
|
||||
KERNEL_OFFSET = 0; /* 0xFFFFFF8000000000; */
|
||||
KERNEL_OFFSET = 0xFFFFFF8000000000;
|
||||
BASE_OFFSET = 0x40080000;
|
||||
|
||||
SECTIONS {
|
||||
. = 0x40080000 + KERNEL_OFFSET;
|
||||
. = BASE_OFFSET;
|
||||
|
||||
.text.lower : {
|
||||
*(.text._entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_OFFSET;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : AT(. - KERNEL_OFFSET) {
|
||||
*(.text._entry)
|
||||
*(.text._entry_upper)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
@ -23,13 +31,14 @@ 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 = .);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ test = false
|
||||
cfg-if = "1.x.x"
|
||||
error = { path = "../error" }
|
||||
tock-registers = "0.7.x"
|
||||
fdt-rs = { version = "0.x.x", default-features = false }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
cortex-a = { version = "6.x.x" }
|
||||
|
@ -1,39 +0,0 @@
|
||||
// vi:ft=a64asm.asm:
|
||||
|
||||
.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:
|
16
kernel/src/arch/aarch64/boot/macros.S
Normal file
16
kernel/src/arch/aarch64/boot/macros.S
Normal file
@ -0,0 +1,16 @@
|
||||
.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
|
@ -1,13 +1,14 @@
|
||||
//! aarch64 common boot logic
|
||||
|
||||
use crate::arch::{aarch64::asm::CPACR_EL1, machine};
|
||||
use crate::dev::Device;
|
||||
use crate::dev::{Device, fdt::DeviceTree};
|
||||
use crate::mem::virt;
|
||||
use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use cortex_a::registers::{DAIF, SCTLR_EL1, VBAR_EL1};
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
|
||||
#[no_mangle]
|
||||
fn __aa64_bsp_main() {
|
||||
extern "C" fn __aa64_bsp_main(fdt_base: usize) {
|
||||
// Disable FP instruction trapping
|
||||
CPACR_EL1.modify(CPACR_EL1::FPEN::TrapNone);
|
||||
|
||||
@ -28,20 +29,33 @@ fn __aa64_bsp_main() {
|
||||
isb(barrier::SY);
|
||||
}
|
||||
|
||||
// Enable MMU
|
||||
virt::enable().expect("Failed to initialize virtual memory");
|
||||
|
||||
machine::init_board().unwrap();
|
||||
|
||||
if fdt_base != 0 {
|
||||
debugln!("fdt_base = {:#x}", fdt_base);
|
||||
let fdt = DeviceTree::from_phys(fdt_base + 0xFFFFFF8000000000);
|
||||
if let Ok(fdt) = fdt {
|
||||
fdt.dump();
|
||||
} else {
|
||||
debugln!("Failed to init FDT");
|
||||
}
|
||||
}
|
||||
|
||||
debugln!("Machine init finished");
|
||||
|
||||
unsafe {
|
||||
machine::local_timer().enable().unwrap();
|
||||
}
|
||||
|
||||
loop {
|
||||
DAIF.modify(DAIF::I::CLEAR);
|
||||
cortex_a::asm::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "mach_orangepi3")] {
|
||||
global_asm!(include_str!("uboot.S"));
|
||||
} else {
|
||||
global_asm!(include_str!("entry.S"));
|
||||
}
|
||||
}
|
||||
global_asm!(include_str!("macros.S"));
|
||||
global_asm!(include_str!("uboot.S"));
|
||||
global_asm!(include_str!("upper.S"));
|
||||
|
@ -10,19 +10,11 @@
|
||||
.set CNTHCTL_EL2_EL1PCEN, 1 << 1
|
||||
.set CNTHCTL_EL2_EL1PCTEN, 1 << 0
|
||||
|
||||
.macro ADR_REL reg, sym
|
||||
adrp \reg, \sym
|
||||
add \reg, \reg, #:lo12:\sym
|
||||
.endm
|
||||
|
||||
.macro MOV_L reg, value
|
||||
mov \reg, #((\value) & 0xFFFF)
|
||||
movk \reg, #((\value) >> 16), lsl #16
|
||||
.endm
|
||||
|
||||
.section .text._entry
|
||||
.global _entry
|
||||
_entry:
|
||||
mov x8, x0
|
||||
|
||||
// Test for EL2
|
||||
mrs x0, CurrentEL
|
||||
lsr x0, x0, #2
|
||||
@ -45,7 +37,7 @@ _entry:
|
||||
orr x0, x0, #SPSR_EL2_MASK_DAIF
|
||||
msr spsr_el2, x0
|
||||
|
||||
ADR_REL x0, 1f
|
||||
adr x0, 1f
|
||||
msr elr_el2, x0
|
||||
|
||||
isb
|
||||
@ -54,8 +46,27 @@ _entry:
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
ADR_REL x0, bsp_stack_top
|
||||
mov sp, x0
|
||||
// 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
|
||||
|
||||
.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
|
||||
|
95
kernel/src/arch/aarch64/boot/upper.S
Normal file
95
kernel/src/arch/aarch64/boot/upper.S
Normal file
@ -0,0 +1,95 @@
|
||||
// vi:ft=a64asm:
|
||||
|
||||
.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)
|
||||
|
||||
.section .text._entry
|
||||
.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
|
@ -1,5 +1,5 @@
|
||||
use crate::arch::MemoryIo;
|
||||
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};
|
||||
@ -22,7 +22,7 @@ register_bitfields! {
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
GiccRegs {
|
||||
pub(super) GiccRegs {
|
||||
(0x00 => CTLR: ReadWrite<u32, CTLR::Register>),
|
||||
(0x04 => PMR: ReadWrite<u32, PMR::Register>),
|
||||
(0x08 => _res0),
|
||||
@ -33,13 +33,13 @@ register_structs! {
|
||||
}
|
||||
|
||||
pub(super) struct Gicc {
|
||||
regs: MemoryIo<GiccRegs>,
|
||||
regs: DeviceMemoryIo<GiccRegs>,
|
||||
}
|
||||
|
||||
impl Gicc {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
pub const unsafe fn new(regs: DeviceMemoryIo<GiccRegs>) -> Self {
|
||||
Self {
|
||||
regs: MemoryIo::new(base),
|
||||
regs,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
@ -22,7 +22,7 @@ register_bitfields! {
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
GicdSharedRegs {
|
||||
pub(super) GicdSharedRegs {
|
||||
(0x000 => CTLR: ReadWrite<u32, CTLR::Register>),
|
||||
(0x004 => TYPER: ReadWrite<u32, TYPER::Register>),
|
||||
(0x008 => _res0),
|
||||
@ -37,7 +37,7 @@ register_structs! {
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
GicdBankedRegs {
|
||||
pub(super) GicdBankedRegs {
|
||||
(0x000 => _res0),
|
||||
(0x100 => ISENABLER: ReadWrite<u32>),
|
||||
(0x104 => _res1),
|
||||
@ -63,15 +63,18 @@ impl GicdSharedRegs {
|
||||
}
|
||||
|
||||
pub(super) struct Gicd {
|
||||
shared_regs: IrqSafeNullLock<MemoryIo<GicdSharedRegs>>,
|
||||
banked_regs: MemoryIo<GicdBankedRegs>,
|
||||
shared_regs: IrqSafeNullLock<DeviceMemoryIo<GicdSharedRegs>>,
|
||||
banked_regs: DeviceMemoryIo<GicdBankedRegs>,
|
||||
}
|
||||
|
||||
impl Gicd {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
pub const unsafe fn new(
|
||||
shared_mmio: DeviceMemoryIo<GicdSharedRegs>,
|
||||
banked_mmio: DeviceMemoryIo<GicdBankedRegs>,
|
||||
) -> Self {
|
||||
Self {
|
||||
shared_regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
banked_regs: MemoryIo::new(base),
|
||||
shared_regs: IrqSafeNullLock::new(shared_mmio),
|
||||
banked_regs: banked_mmio,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,9 @@ use crate::dev::{
|
||||
irq::{IntController, IntSource, IrqContext},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::{DeviceMemoryIo, DeviceMemory};
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
|
||||
mod gicc;
|
||||
@ -22,8 +24,10 @@ pub struct IrqNumber(usize);
|
||||
|
||||
/// ARM Generic Interrupt Controller, version 2
|
||||
pub struct Gic {
|
||||
gicc: Gicc,
|
||||
gicd: Gicd,
|
||||
gicc: InitOnce<Gicc>,
|
||||
gicd: InitOnce<Gicd>,
|
||||
gicd_base: usize,
|
||||
gicc_base: usize,
|
||||
table: IrqSafeNullLock<[Option<&'static (dyn IntSource + Sync)>; MAX_IRQ]>,
|
||||
}
|
||||
|
||||
@ -48,8 +52,20 @@ impl Device for Gic {
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.gicd.enable();
|
||||
self.gicc.enable();
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -58,12 +74,13 @@ impl IntController for Gic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno> {
|
||||
self.gicd.enable_irq(irq);
|
||||
self.gicd.get().enable_irq(irq);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
|
||||
let irq_number = self.gicc.pending_irq_number(ic);
|
||||
let gicc = self.gicc.get();
|
||||
let irq_number = gicc.pending_irq_number(ic);
|
||||
if irq_number >= MAX_IRQ {
|
||||
return;
|
||||
}
|
||||
@ -76,7 +93,7 @@ impl IntController for Gic {
|
||||
}
|
||||
}
|
||||
|
||||
self.gicc.clear_irq(irq_number as u32, ic);
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
@ -105,8 +122,10 @@ impl Gic {
|
||||
/// Does not perform `gicd_base` and `gicc_base` validation.
|
||||
pub const unsafe fn new(gicd_base: usize, gicc_base: usize) -> Self {
|
||||
Self {
|
||||
gicc: Gicc::new(gicc_base),
|
||||
gicd: Gicd::new(gicd_base),
|
||||
gicc: InitOnce::new(),
|
||||
gicd: InitOnce::new(),
|
||||
gicd_base,
|
||||
gicc_base,
|
||||
table: IrqSafeNullLock::new([None; MAX_IRQ]),
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@
|
||||
//! 1. CPUS-PORT (TODO PL, PM)
|
||||
//! 2. CPUX-PORT (PC, PD, PF, PG, PH)
|
||||
//!
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::dev::{
|
||||
gpio::{GpioDevice, PinConfig, PinMode, PullMode},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
@ -28,11 +29,12 @@ register_structs! {
|
||||
}
|
||||
|
||||
struct CpuxGpio {
|
||||
regs: MemoryIo<[CpuxPortRegs; 8]>,
|
||||
regs: DeviceMemoryIo<[CpuxPortRegs; 8]>,
|
||||
}
|
||||
|
||||
pub struct Gpio {
|
||||
cpux: IrqSafeNullLock<CpuxGpio>,
|
||||
cpux: InitOnce<IrqSafeNullLock<CpuxGpio>>,
|
||||
cpux_base: usize,
|
||||
}
|
||||
|
||||
/// Structure combining bank and pin numbers
|
||||
@ -142,6 +144,9 @@ impl Device for Gpio {
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.cpux.init(IrqSafeNullLock::new(CpuxGpio {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.cpux_base, 1)?,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -155,7 +160,7 @@ impl GpioDevice for Gpio {
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => self.cpux.lock().set_pin_config(bank, pin, cfg),
|
||||
_ => self.cpux.get().lock().set_pin_config(bank, pin, cfg),
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +174,7 @@ impl GpioDevice for Gpio {
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => self.cpux.lock().write_pin(bank, pin, state),
|
||||
_ => self.cpux.get().lock().write_pin(bank, pin, state),
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +184,7 @@ impl GpioDevice for Gpio {
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => self.cpux.lock().toggle_pin(bank, pin),
|
||||
_ => self.cpux.get().lock().toggle_pin(bank, pin),
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +194,7 @@ impl GpioDevice for Gpio {
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => Ok(self.cpux.lock().read_pin(bank, pin)),
|
||||
_ => Ok(self.cpux.get().lock().read_pin(bank, pin)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,11 +205,10 @@ impl Gpio {
|
||||
self.set_pin_config(PinAddress::new(7, 1), &PinConfig::alt(2))
|
||||
}
|
||||
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
pub const unsafe fn new(cpux_base: usize) -> Self {
|
||||
Self {
|
||||
cpux: IrqSafeNullLock::new(CpuxGpio {
|
||||
regs: MemoryIo::new(base),
|
||||
}),
|
||||
cpux: InitOnce::new(),
|
||||
cpux_base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,14 +28,17 @@ use wdog::RWdog;
|
||||
#[allow(missing_docs)]
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
UART0.enable()?;
|
||||
GIC.enable()?;
|
||||
GPIO.enable()?;
|
||||
|
||||
UART0.init_irqs()?;
|
||||
|
||||
R_WDOG.enable()?;
|
||||
|
||||
GPIO.cfg_uart0_ph0_ph1()?;
|
||||
GPIO.set_pin_config(PinAddress::new(3, 26), &PinConfig::out_pull_down())?;
|
||||
|
||||
UART0.enable()?;
|
||||
UART0.init_irqs()?;
|
||||
|
||||
RTC.enable()?;
|
||||
RTC.init_irqs()?;
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
use crate::arch::{
|
||||
machine::{self, IrqNumber},
|
||||
MemoryIo,
|
||||
};
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
rtc::RtcDevice,
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
@ -48,7 +47,8 @@ register_structs! {
|
||||
}
|
||||
|
||||
pub struct Rtc {
|
||||
regs: IrqSafeNullLock<MemoryIo<Regs>>,
|
||||
regs: InitOnce<IrqSafeNullLock<DeviceMemoryIo<Regs>>>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
@ -70,13 +70,14 @@ impl RtcDevice for Rtc {}
|
||||
|
||||
impl IntSource for Rtc {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
self.regs.lock().arm_alarm0_irq(1);
|
||||
self.regs.get().lock().arm_alarm0_irq(1);
|
||||
debugln!("Tick!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.regs.lock().arm_alarm0_irq(1);
|
||||
self.regs.get().lock().arm_alarm0_irq(1);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
@ -89,6 +90,7 @@ impl Device for Rtc {
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.regs.init(IrqSafeNullLock::new(DeviceMemoryIo::map(self.name(), self.base, 1)?));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -101,7 +103,8 @@ impl Rtc {
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
regs: InitOnce::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::arch::{
|
||||
machine::{self, IrqNumber},
|
||||
MemoryIo,
|
||||
};
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
@ -73,8 +74,13 @@ register_structs! {
|
||||
}
|
||||
}
|
||||
|
||||
struct UartInner {
|
||||
regs: DeviceMemoryIo<Regs>
|
||||
}
|
||||
|
||||
pub(super) struct Uart {
|
||||
regs: IrqSafeNullLock<MemoryIo<Regs>>,
|
||||
inner: InitOnce<IrqSafeNullLock<UartInner>>,
|
||||
base: usize,
|
||||
irq: IrqNumber
|
||||
}
|
||||
|
||||
@ -84,33 +90,41 @@ impl Device for Uart {
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let mut inner = UartInner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?
|
||||
};
|
||||
// TODO
|
||||
self.inner.init(IrqSafeNullLock::new(inner));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Uart {
|
||||
fn send(&self, byte: u8) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
while !regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
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();
|
||||
}
|
||||
regs.DR_DLL.set(byte as u32);
|
||||
inner.regs.DR_DLL.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&self, _blocking: bool) -> Result<u8, Errno> {
|
||||
let regs = self.regs.lock();
|
||||
while !regs.LSR.matches_all(LSR::DR::SET) {
|
||||
let inner = self.inner.get().lock();
|
||||
while !inner.regs.LSR.matches_all(LSR::DR::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
Ok(regs.DR_DLL.get() as u8)
|
||||
Ok(inner.regs.DR_DLL.get() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSource for Uart {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let byte = self.regs.lock().DR_DLL.get();
|
||||
let byte = self.inner.get().lock().regs.DR_DLL.get();
|
||||
debugln!("irq byte = {:#04x}!", byte);
|
||||
|
||||
if byte == 0x1B {
|
||||
@ -127,7 +141,7 @@ impl IntSource for Uart {
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.regs.lock().IER_DLH.modify(IER::ERBFI::SET);
|
||||
self.inner.get().lock().regs.IER_DLH.modify(IER::ERBFI::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
@ -137,7 +151,8 @@ impl IntSource for Uart {
|
||||
impl Uart {
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
irq
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite,
|
||||
};
|
||||
@ -36,7 +39,23 @@ register_structs! {
|
||||
}
|
||||
|
||||
pub(super) struct RWdog {
|
||||
regs: IrqSafeNullLock<MemoryIo<RWdogRegs>>,
|
||||
inner: InitOnce<IrqSafeNullLock<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(IrqSafeNullLock::new(DeviceMemoryIo::map(
|
||||
self.name(),
|
||||
self.base,
|
||||
1,
|
||||
)?));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RWdog {
|
||||
@ -46,7 +65,7 @@ impl RWdog {
|
||||
///
|
||||
/// Unsafe: may interrupt critical processes
|
||||
pub unsafe fn reset_board(&self) -> ! {
|
||||
let regs = self.regs.lock();
|
||||
let regs = self.inner.get().lock();
|
||||
|
||||
regs.CFG.write(CFG::CONFIG::System);
|
||||
regs.MODE.write(MODE::EN::SET);
|
||||
@ -64,7 +83,8 @@ impl RWdog {
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crate::arch::aarch64::{
|
||||
use crate::dev::timer::TimestampSource;
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
pci::{PciHostDevice, pcie::gpex::GenericPcieHost},
|
||||
pci::{pcie::gpex::GenericPcieHost, PciHostDevice},
|
||||
rtc::pl031::Pl031,
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
@ -26,9 +26,10 @@ const ECAM_BASE: usize = 0x4010000000;
|
||||
#[allow(missing_docs)]
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
UART0.enable()?;
|
||||
|
||||
GIC.enable()?;
|
||||
|
||||
UART0.enable()?;
|
||||
UART0.init_irqs()?;
|
||||
|
||||
RTC.enable()?;
|
||||
|
@ -19,34 +19,34 @@ 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::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 _) }
|
||||
// }
|
||||
// }
|
||||
|
93
kernel/src/dev/fdt.rs
Normal file
93
kernel/src/dev/fdt.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use error::Errno;
|
||||
use fdt_rs::prelude::*;
|
||||
use fdt_rs::{
|
||||
base::DevTree,
|
||||
index::{DevTreeIndex, DevTreeIndexNode},
|
||||
};
|
||||
|
||||
#[repr(align(16))]
|
||||
struct Wrap {
|
||||
data: [u8; 65536],
|
||||
}
|
||||
|
||||
static mut INDEX_BUFFER: Wrap = Wrap { data: [0; 65536] };
|
||||
|
||||
type INode<'a> = DevTreeIndexNode<'a, 'a, 'a>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct DeviceTree {
|
||||
tree: DevTree<'static>,
|
||||
index: DevTreeIndex<'static, 'static>,
|
||||
}
|
||||
|
||||
fn tab(depth: usize) {
|
||||
for _ in 0..depth {
|
||||
debug!("\t");
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_node(node: &INode, depth: usize) {
|
||||
if node.name().unwrap().starts_with("virtio_mmio@") {
|
||||
return;
|
||||
}
|
||||
|
||||
tab(depth);
|
||||
debugln!("{:?} {{", node.name().unwrap());
|
||||
|
||||
for prop in node.props() {
|
||||
tab(depth + 1);
|
||||
let name = prop.name().unwrap();
|
||||
debug!("{:?} = ", name);
|
||||
|
||||
match name {
|
||||
"compatible" => debug!("{:?}", prop.str().unwrap()),
|
||||
"#address-cells" | "#size-cells" => debug!("{}", prop.u32(0).unwrap()),
|
||||
"reg" => {
|
||||
debug!("<");
|
||||
let len = prop.length() / 4;
|
||||
for i in 0..len {
|
||||
debug!("{:#010x}", prop.u32(i).unwrap());
|
||||
if i < len - 1 {
|
||||
debug!(", ");
|
||||
}
|
||||
}
|
||||
debug!(">");
|
||||
}
|
||||
_ => debug!("..."),
|
||||
}
|
||||
debugln!(";");
|
||||
}
|
||||
|
||||
if node.children().next().is_some() {
|
||||
debugln!("");
|
||||
}
|
||||
|
||||
for child in node.children() {
|
||||
dump_node(&child, depth + 1);
|
||||
}
|
||||
|
||||
tab(depth);
|
||||
debugln!("}}");
|
||||
}
|
||||
|
||||
impl DeviceTree {
|
||||
pub fn dump(&self) {
|
||||
dump_node(&self.index.root(), 0);
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
use error::Errno;
|
||||
|
||||
// Device classes
|
||||
#[allow(missing_docs)]
|
||||
pub mod fdt;
|
||||
pub mod gpio;
|
||||
pub mod irq;
|
||||
pub mod pci;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
//! PCI bus host and device interfaces
|
||||
|
||||
use crate::dev::Device;
|
||||
use core::fmt;
|
||||
@ -8,18 +8,21 @@ pub mod pcie;
|
||||
|
||||
macro_rules! ecam_field {
|
||||
($getter:ident, $off:expr, u16) => {
|
||||
#[allow(missing_docs)]
|
||||
#[inline(always)]
|
||||
fn $getter(&self) -> u16 {
|
||||
self.readw($off)
|
||||
}
|
||||
};
|
||||
($getter:ident, $off:expr, u8) => {
|
||||
#[allow(missing_docs)]
|
||||
#[inline(always)]
|
||||
fn $getter(&self) -> u8 {
|
||||
self.readb($off)
|
||||
}
|
||||
};
|
||||
($getter:ident, $setter:ident, $off:expr, u16) => {
|
||||
#[allow(missing_docs)]
|
||||
#[inline(always)]
|
||||
unsafe fn $setter(&self, v: u16) {
|
||||
self.writew($off, v)
|
||||
@ -29,22 +32,37 @@ macro_rules! ecam_field {
|
||||
};
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -54,17 +72,22 @@ pub trait PciCfgSpace {
|
||||
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 {
|
||||
@ -72,21 +95,26 @@ impl PciAddress {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
|
@ -1,11 +1,17 @@
|
||||
//! Generic PCIe host driver
|
||||
|
||||
use crate::dev::{
|
||||
pci::{pcie::EcamCfgSpace, PciCfgSpace, PciAddress, PciHostDevice},
|
||||
pci::{pcie::EcamCfgSpace, PciAddress, PciCfgSpace, PciHostDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemory;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
|
||||
/// GPEX host controller struct
|
||||
pub struct GenericPcieHost {
|
||||
ecam_base: usize,
|
||||
ecam: InitOnce<DeviceMemory>,
|
||||
// TODO
|
||||
#[allow(dead_code)]
|
||||
bus_count: u8,
|
||||
@ -17,13 +23,15 @@ impl Device for GenericPcieHost {
|
||||
}
|
||||
|
||||
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 = unsafe { EcamCfgSpace::new(self.ecam_base, PciAddress::new(0, 0, 0)) };
|
||||
let bus0 = self.get_ecam(PciAddress::new(0, 0, 0));
|
||||
|
||||
if bus0.header_type() & 0x80 == 0 {
|
||||
self.map_bus(0)?;
|
||||
@ -36,22 +44,37 @@ impl PciHostDevice for GenericPcieHost {
|
||||
}
|
||||
|
||||
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> {
|
||||
debugln!(
|
||||
"{:?}: {:04x}:{:04x}",
|
||||
addr,
|
||||
cfg.vendor_id(),
|
||||
cfg.device_id()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_device(&self, addr: PciAddress) -> Result<(), Errno> {
|
||||
let fn0 = unsafe { EcamCfgSpace::new(self.ecam_base, addr) };
|
||||
let fn0 = self.get_ecam(addr);
|
||||
if !fn0.is_valid() {
|
||||
return Ok(());
|
||||
}
|
||||
let ty = fn0.header_type();
|
||||
|
||||
//self.map_function(addr, fn0)?;
|
||||
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 = unsafe { EcamCfgSpace::new(self.ecam_base, addr) };
|
||||
let f = self.get_ecam(addr);
|
||||
if f.is_valid() {
|
||||
//self.map_function(addr, f)?;
|
||||
self.map_function(addr, f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,8 +90,14 @@ impl GenericPcieHost {
|
||||
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,7 +1,10 @@
|
||||
//! 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 {
|
||||
@ -9,9 +12,14 @@ pub struct EcamCfgSpace {
|
||||
}
|
||||
|
||||
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
|
||||
base: ecam_base + (addr.value as usize) * 4096,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,9 +28,7 @@ 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)
|
||||
}
|
||||
unsafe { core::ptr::read_volatile((self.base + off) as *const u32) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1,10 +1,16 @@
|
||||
//! PL031 - ARM PrimeCell real-time clock implementation
|
||||
use crate::dev::{Device, rtc::RtcDevice, irq::{IntController, IntSource}};
|
||||
use crate::arch::{MemoryIo, machine::{self, IrqNumber}};
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
rtc::RtcDevice,
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable, ReadWriteable},
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
@ -37,27 +43,31 @@ register_structs! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Device struct for PL031
|
||||
pub struct Pl031 {
|
||||
regs: IrqSafeNullLock<MemoryIo<Regs>>,
|
||||
irq: IrqNumber
|
||||
struct Pl031Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
}
|
||||
|
||||
impl RtcDevice for Pl031 {
|
||||
/// Device struct for PL031
|
||||
pub struct Pl031 {
|
||||
inner: InitOnce<IrqSafeNullLock<Pl031Inner>>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl RtcDevice for Pl031 {}
|
||||
|
||||
impl IntSource for Pl031 {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
regs.ICR.write(ICR::RTCICR::SET);
|
||||
let data = regs.DR.get();
|
||||
regs.MR.set(data + 1);
|
||||
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.regs.lock().IMSC.modify(IMSC::RTCIMSC::SET);
|
||||
self.inner.get().lock().regs.IMSC.modify(IMSC::RTCIMSC::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
@ -70,10 +80,16 @@ impl Device for Pl031 {
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
regs.CR.modify(CR::RTCStart::CLEAR);
|
||||
regs.MR.set(regs.DR.get() + 1);
|
||||
regs.CR.modify(CR::RTCStart::SET);
|
||||
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(IrqSafeNullLock::new(inner));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -86,8 +102,9 @@ impl Pl031 {
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
irq
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,18 @@
|
||||
//! PL011 - ARM PrimeCell UART implementation
|
||||
|
||||
use crate::arch::{MemoryIo, machine::{self, IrqNumber}};
|
||||
use crate::dev::{serial::SerialDevice, irq::{IntController, IntSource}, Device};
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::fmt;
|
||||
use error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable, ReadWriteable},
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
@ -64,27 +71,77 @@ register_structs! {
|
||||
}
|
||||
}
|
||||
|
||||
struct Pl011Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
}
|
||||
|
||||
/// Device struct for PL011
|
||||
pub struct Pl011 {
|
||||
regs: IrqSafeNullLock<MemoryIo<Regs>>,
|
||||
inner: InitOnce<IrqSafeNullLock<Pl011Inner>>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl Pl011Inner {
|
||||
///
|
||||
#[inline(always)]
|
||||
pub unsafe fn send(&mut self, byte: u8) {
|
||||
while self.regs.FR.matches_all(FR::TXFF::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte as u32);
|
||||
}
|
||||
|
||||
///
|
||||
pub 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) {
|
||||
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 regs = self.regs.lock();
|
||||
regs.ICR.write(ICR::ALL::CLEAR);
|
||||
let inner = self.inner.get().lock();
|
||||
inner.regs.ICR.write(ICR::ALL::CLEAR);
|
||||
|
||||
let byte = regs.DR.get();
|
||||
let byte = inner.regs.DR.get();
|
||||
debugln!("irq byte = {:#04x}", byte);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.regs.lock().IMSC.modify(IMSC::RXIM::SET);
|
||||
self.inner.get().lock().regs.IMSC.modify(IMSC::RXIM::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
@ -93,27 +150,18 @@ impl IntSource for Pl011 {
|
||||
|
||||
impl SerialDevice for Pl011 {
|
||||
fn send(&self, byte: u8) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
while regs.FR.matches_all(FR::TXFF::SET) {
|
||||
core::hint::spin_loop();
|
||||
if !self.inner.is_initialized() {
|
||||
// TODO early output here
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
self.inner.get().lock().send(byte);
|
||||
}
|
||||
regs.DR.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&self, blocking: bool) -> Result<u8, Errno> {
|
||||
let regs = self.regs.lock();
|
||||
if regs.FR.matches_all(FR::RXFE::SET) {
|
||||
if !blocking {
|
||||
return Err(Errno::WouldBlock);
|
||||
}
|
||||
while regs.FR.matches_all(FR::RXFE::SET) {
|
||||
// TODO allow IRQs here?
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(regs.DR.get() as u8)
|
||||
unsafe { self.inner.get().lock().recv(blocking) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,10 +171,12 @@ impl Device for Pl011 {
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let regs = self.regs.lock();
|
||||
regs.CR.set(0);
|
||||
regs.ICR.write(ICR::ALL::CLEAR);
|
||||
regs.CR.write(CR::UARTEN::SET + CR::TXE::SET + CR::RXE::SET);
|
||||
let mut inner = Pl011Inner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
};
|
||||
inner.enable();
|
||||
|
||||
self.inner.init(IrqSafeNullLock::new(inner));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -140,8 +190,9 @@ impl Pl011 {
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: IrqSafeNullLock::new(MemoryIo::new(base)),
|
||||
irq
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,18 +19,16 @@ extern crate cfg_if;
|
||||
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
|
||||
pub mod arch;
|
||||
pub mod dev;
|
||||
pub mod mem;
|
||||
pub mod sync;
|
||||
pub mod util;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
if let Some(msg) = pi.message() {
|
||||
debugln!("Panic occurred: {:?}", msg);
|
||||
} else {
|
||||
debugln!("Panic occurred");
|
||||
}
|
||||
debugln!("Panic location: {:?}", pi.location());
|
||||
debugln!("Panic: {:?}", pi);
|
||||
// TODO
|
||||
loop {}
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
//! Memory management and functions module
|
||||
|
||||
pub mod virt;
|
||||
|
||||
/// Virtual offset applied to kernel address space
|
||||
pub const KERNEL_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
|
||||
/// See memcpy(3p).
|
||||
///
|
||||
/// # Safety
|
||||
|
169
kernel/src/mem/virt/mod.rs
Normal file
169
kernel/src/mem/virt/mod.rs
Normal file
@ -0,0 +1,169 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::mem::KERNEL_OFFSET;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Deref;
|
||||
use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use cortex_a::registers::{TCR_EL1, TTBR0_EL1};
|
||||
use error::Errno;
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
|
||||
const PTE_BLOCK_AF: u64 = 1 << 10;
|
||||
const PTE_BLOCK_OSH: u64 = 2 << 8;
|
||||
const PTE_TABLE: u64 = 1 << 1;
|
||||
const PTE_PRESENT: u64 = 1 << 0;
|
||||
const PTE_ATTR1: u64 = 1 << 2;
|
||||
|
||||
#[repr(C, align(0x1000))]
|
||||
struct Table([u64; 512]);
|
||||
|
||||
#[no_mangle]
|
||||
static mut KERNEL_TTBR1: Table = Table([0; 512]);
|
||||
// 1GiB
|
||||
static mut KERNEL_L1: Table = Table([0; 512]);
|
||||
// 2MiB
|
||||
static mut KERNEL_L2: Table = Table([0; 512]);
|
||||
static mut COUNT: usize = 0;
|
||||
static mut BIG_COUNT: usize = 1;
|
||||
static mut HUGE_COUNT: usize = 1;
|
||||
|
||||
const DEVICE_MAP_OFFSET: usize = KERNEL_OFFSET + (256 << 30);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct DeviceMemory {
|
||||
name: &'static str,
|
||||
base: usize,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
pub struct DeviceMemoryIo<T> {
|
||||
mmio: DeviceMemory,
|
||||
_0: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl DeviceMemory {
|
||||
#[inline(always)]
|
||||
pub const fn base(&self) -> usize {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
|
||||
// TODO generalize this
|
||||
let phys_page = phys & !0xFFF;
|
||||
|
||||
let base = unsafe {
|
||||
match count {
|
||||
262144 => {
|
||||
let count = HUGE_COUNT;
|
||||
if count == 512 {
|
||||
return Err(Errno::OutOfMemory);
|
||||
}
|
||||
HUGE_COUNT += 1;
|
||||
|
||||
KERNEL_TTBR1.0[count + 256] =
|
||||
(phys_page as u64) | PTE_PRESENT | PTE_BLOCK_OSH | PTE_BLOCK_AF | PTE_ATTR1;
|
||||
asm!("dsb ish; isb");
|
||||
|
||||
DEVICE_MAP_OFFSET + (count << 30) + (phys & 0xFFF)
|
||||
}
|
||||
512 => {
|
||||
let count = BIG_COUNT;
|
||||
if count == 512 {
|
||||
return Err(Errno::OutOfMemory);
|
||||
}
|
||||
BIG_COUNT += 1;
|
||||
|
||||
KERNEL_L1.0[count] =
|
||||
(phys_page as u64) | PTE_PRESENT | PTE_BLOCK_OSH | PTE_BLOCK_AF | PTE_ATTR1;
|
||||
asm!("dsb ish; isb");
|
||||
|
||||
DEVICE_MAP_OFFSET + (count << 21) + (phys & 0xFFF)
|
||||
}
|
||||
1 => {
|
||||
let count = COUNT;
|
||||
if count == 512 {
|
||||
return Err(Errno::OutOfMemory);
|
||||
}
|
||||
COUNT += 1;
|
||||
|
||||
KERNEL_L2.0[count] = (phys_page as u64)
|
||||
| PTE_TABLE
|
||||
| PTE_BLOCK_OSH
|
||||
| PTE_PRESENT
|
||||
| PTE_BLOCK_AF
|
||||
| PTE_ATTR1;
|
||||
asm!("dsb ish; isb");
|
||||
|
||||
DEVICE_MAP_OFFSET + (count << 12) + (phys & 0xFFF)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
};
|
||||
|
||||
debugln!(
|
||||
"Mapping {:#x}..{:#x} -> {:#x} ({:?})",
|
||||
base,
|
||||
base + count * 0x1000,
|
||||
phys,
|
||||
name
|
||||
);
|
||||
|
||||
Ok(Self { name, base, count })
|
||||
}
|
||||
|
||||
pub unsafe fn clone(&self) -> Self {
|
||||
// TODO maybe add refcount and remove "unsafe"?
|
||||
Self {
|
||||
name: self.name,
|
||||
base: self.base,
|
||||
count: self.count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DeviceMemoryIo<T> {
|
||||
pub const fn new(mmio: DeviceMemory) -> Self {
|
||||
Self {
|
||||
mmio,
|
||||
_0: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
|
||||
DeviceMemory::map(name, phys, count).map(Self::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for DeviceMemoryIo<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*(self.mmio.base as *const T) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
// TODO function to translate kernel addresses to physical ones
|
||||
let l1_base = (&KERNEL_L1 as *const _ as u64) - KERNEL_OFFSET as u64;
|
||||
let l2_base = (&KERNEL_L2 as *const _ as u64) - KERNEL_OFFSET as u64;
|
||||
|
||||
KERNEL_L1.0[0] = l2_base | PTE_TABLE | PTE_PRESENT;
|
||||
KERNEL_TTBR1.0[256] = l1_base | PTE_TABLE | PTE_PRESENT;
|
||||
|
||||
// NOTE don't think tlb needs to be invalidated when new entries are created
|
||||
}
|
||||
|
||||
unsafe {
|
||||
dsb(barrier::ISH);
|
||||
isb(barrier::SY);
|
||||
}
|
||||
|
||||
// Disable lower-half translation
|
||||
TTBR0_EL1.set(0);
|
||||
TCR_EL1.modify(TCR_EL1::EPD0::SET);
|
||||
|
||||
Ok(())
|
||||
}
|
45
kernel/src/util.rs
Normal file
45
kernel/src/util.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub struct InitOnce<T> {
|
||||
state: AtomicBool,
|
||||
inner: UnsafeCell<MaybeUninit<T>>,
|
||||
}
|
||||
|
||||
impl<T> InitOnce<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: AtomicBool::new(false),
|
||||
inner: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn get(&self) -> &mut T {
|
||||
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>");
|
||||
unsafe { (*self.inner.get()).assume_init_mut() }
|
||||
}
|
||||
|
||||
pub fn init(&self, value: T) {
|
||||
assert!(
|
||||
self.state
|
||||
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_ok(),
|
||||
"Double-initialization of InitOnce<T>"
|
||||
);
|
||||
|
||||
unsafe {
|
||||
(*self.inner.get()).write(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for InitOnce<T> {}
|
Loading…
x
Reference in New Issue
Block a user