From fe4c2f7c77d44455d3742cf83089d9456d5b7602 Mon Sep 17 00:00:00 2001
From: Mark Poliakov <mark@alnyan.me>
Date: Thu, 19 Aug 2021 18:33:58 +0300
Subject: [PATCH] AA

---
 Cargo.lock                                  |  44 +++
 Cargo.toml                                  |   9 +-
 address/Cargo.toml                          |   9 +
 address/src/base/mod.rs                     |   0
 address/src/lib.rs                          |  19 ++
 address/src/phys.rs                         | 154 +++++++++
 address/src/virt.rs                         | 344 ++++++++++++++++++++
 error/Cargo.toml                            |   8 +
 error/src/lib.rs                            |   6 +
 etc/aarch64-unknown-none-rpi3b.ld           |  13 +-
 etc/gdbrc                                   |   2 +-
 {.cargo => kernel/.cargo}/config.toml       |   2 +-
 kernel/Cargo.toml                           |  11 +
 src/main.rs => kernel/src/arch/exception.rs |  25 +-
 kernel/src/arch/mod.rs                      |   1 +
 kernel/src/arch/vectors.S                   |  75 +++++
 {src => kernel/src}/boot/entry.S            |  84 +----
 {src => kernel/src}/boot/mod.rs             |   0
 kernel/src/debug.rs                         |  28 ++
 kernel/src/main.rs                          |  61 ++++
 kernel/src/mem/address.rs                   |   0
 kernel/src/mem/mod.rs                       |  21 ++
 kernel/src/mem/phys/manager.rs              |  62 ++++
 kernel/src/mem/phys/mod.rs                  | 118 +++++++
 kernel/src/mem/phys/pbox.rs                 |  24 ++
 kernel/src/mem/phys/reserved.rs             |  79 +++++
 src/lib.rs                                  |   0
 27 files changed, 1090 insertions(+), 109 deletions(-)
 create mode 100644 address/Cargo.toml
 create mode 100644 address/src/base/mod.rs
 create mode 100644 address/src/lib.rs
 create mode 100644 address/src/phys.rs
 create mode 100644 address/src/virt.rs
 create mode 100644 error/Cargo.toml
 create mode 100644 error/src/lib.rs
 rename {.cargo => kernel/.cargo}/config.toml (55%)
 create mode 100644 kernel/Cargo.toml
 rename src/main.rs => kernel/src/arch/exception.rs (58%)
 create mode 100644 kernel/src/arch/mod.rs
 create mode 100644 kernel/src/arch/vectors.S
 rename {src => kernel/src}/boot/entry.S (70%)
 rename {src => kernel/src}/boot/mod.rs (100%)
 create mode 100644 kernel/src/debug.rs
 create mode 100644 kernel/src/main.rs
 create mode 100644 kernel/src/mem/address.rs
 create mode 100644 kernel/src/mem/mod.rs
 create mode 100644 kernel/src/mem/phys/manager.rs
 create mode 100644 kernel/src/mem/phys/mod.rs
 create mode 100644 kernel/src/mem/phys/pbox.rs
 create mode 100644 kernel/src/mem/phys/reserved.rs
 create mode 100644 src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index fba3096..155c4a5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,50 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "address"
+version = "0.1.0"
+dependencies = [
+ "error",
+]
+
+[[package]]
+name = "error"
+version = "0.1.0"
+
+[[package]]
+name = "kernel"
+version = "0.1.0"
+dependencies = [
+ "address",
+ "error",
+ "spin",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
+dependencies = [
+ "scopeguard",
+]
+
 [[package]]
 name = "osdev4"
 version = "0.1.0"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "spin"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
+dependencies = [
+ "lock_api",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 371f64c..be072a9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,9 @@ name = "osdev4"
 version = "0.1.0"
 edition = "2018"
 
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
+[workspace]
+members = [
+    "kernel",
+    "error",
+    "address"
+]
diff --git a/address/Cargo.toml b/address/Cargo.toml
new file mode 100644
index 0000000..73486c9
--- /dev/null
+++ b/address/Cargo.toml
@@ -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" }
diff --git a/address/src/base/mod.rs b/address/src/base/mod.rs
new file mode 100644
index 0000000..e69de29
diff --git a/address/src/lib.rs b/address/src/lib.rs
new file mode 100644
index 0000000..f069084
--- /dev/null
+++ b/address/src/lib.rs
@@ -0,0 +1,19 @@
+//! Type-safe wrappers for different address kinds
+#![no_std]
+#![feature(
+    step_trait,
+    const_fn_trait_bound
+)]
+// #![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};
diff --git a/address/src/phys.rs b/address/src/phys.rs
new file mode 100644
index 0000000..34fadec
--- /dev/null
+++ b/address/src/phys.rs
@@ -0,0 +1,154 @@
+use crate::{AddressSpace, TrivialConvert, VirtualAddress};
+use core::convert::TryFrom;
+use core::fmt;
+use core::iter::Step;
+use core::ops::{Add, AddAssign, Neg, 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 {
+    #[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> From<PhysicalAddress> for VirtualAddress<T> {
+    fn from(p: PhysicalAddress) -> Self {
+        VirtualAddress::from(p.0 + T::OFFSET)
+    }
+}
+
+// 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);
+    }
+}
diff --git a/address/src/virt.rs b/address/src/virt.rs
new file mode 100644
index 0000000..39aa711
--- /dev/null
+++ b/address/src/virt.rs
@@ -0,0 +1,344 @@
+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 unsafe fn as_mut<U>(self) -> &'static mut U {
+        &mut *(self.0 as *mut U)
+    }
+}
+
+// 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> 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);
+    }
+}
diff --git a/error/Cargo.toml b/error/Cargo.toml
new file mode 100644
index 0000000..31b8188
--- /dev/null
+++ b/error/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "error"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/error/src/lib.rs b/error/src/lib.rs
new file mode 100644
index 0000000..4d2f980
--- /dev/null
+++ b/error/src/lib.rs
@@ -0,0 +1,6 @@
+#![no_std]
+
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum Errno {
+
+}
diff --git a/etc/aarch64-unknown-none-rpi3b.ld b/etc/aarch64-unknown-none-rpi3b.ld
index 2fd06f7..9967e78 100644
--- a/etc/aarch64-unknown-none-rpi3b.ld
+++ b/etc/aarch64-unknown-none-rpi3b.ld
@@ -7,18 +7,23 @@ SECTIONS {
 
     .text : AT(. - KERNEL_OFFSET) ALIGN(4K) {
         KEEP(*(.text.boot))
-        *(.text)
+        *(.text*)
     }
 
     .rodata : AT(. - KERNEL_OFFSET) ALIGN(4K) {
-        *(.rodata)
+        *(.rodata*)
     }
 
     .data : AT(. - KERNEL_OFFSET) ALIGN(4K) {
-        *(.data)
+        *(.data*)
     }
 
     .bss : AT(. - KERNEL_OFFSET) ALIGN(4K) {
-        *(.bss)
+        *(COMMON)
+        *(.bss*)
+
+        . = ALIGN(4K);
+        PROVIDE(__kernel_end = .);
+        PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET);
     }
 }
diff --git a/etc/gdbrc b/etc/gdbrc
index f697f0f..ad43054 100644
--- a/etc/gdbrc
+++ b/etc/gdbrc
@@ -1,4 +1,4 @@
-symbol-file target/aarch64-unknown-none-rpi3b/debug/osdev4
+symbol-file target/aarch64-unknown-none-rpi3b/debug/kernel
 target remote :1234
 layout asm
 layout regs
diff --git a/.cargo/config.toml b/kernel/.cargo/config.toml
similarity index 55%
rename from .cargo/config.toml
rename to kernel/.cargo/config.toml
index 1e37b56..44d1694 100644
--- a/.cargo/config.toml
+++ b/kernel/.cargo/config.toml
@@ -2,4 +2,4 @@
 build-std = ["core", "compiler_builtins"]
 
 [build]
-target = "etc/aarch64-unknown-none-rpi3b.json"
+target = "../etc/aarch64-unknown-none-rpi3b.json"
diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml
new file mode 100644
index 0000000..0c38770
--- /dev/null
+++ b/kernel/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "kernel"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+address = { path = "../address" }
+error = { path = "../error" }
+spin = "0.9.2"
diff --git a/src/main.rs b/kernel/src/arch/exception.rs
similarity index 58%
rename from src/main.rs
rename to kernel/src/arch/exception.rs
index efe9507..8880744 100644
--- a/src/main.rs
+++ b/kernel/src/arch/exception.rs
@@ -1,8 +1,4 @@
-#![feature(global_asm, llvm_asm)]
-#![no_std]
-#![no_main]
-
-pub mod boot;
+global_asm!(include_str!("vectors.S"));
 
 #[repr(C)]
 struct ExceptionContext {
@@ -36,22 +32,3 @@ struct ExceptionContext {
 extern "C" fn exc_handler(context: ExceptionContext) -> ! {
     loop {}
 }
-
-#[no_mangle]
-extern "C" fn kernel_main() -> ! {
-    unsafe {
-        let v = *(0x1234 as *mut u64);
-    }
-
-    loop {
-        unsafe {
-            llvm_asm!("wfe");
-        }
-    }
-}
-
-use core::panic::PanicInfo;
-#[panic_handler]
-fn panic_handler(_pi: &PanicInfo) -> ! {
-    loop {}
-}
diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs
new file mode 100644
index 0000000..aa4b15a
--- /dev/null
+++ b/kernel/src/arch/mod.rs
@@ -0,0 +1 @@
+pub mod exception;
diff --git a/kernel/src/arch/vectors.S b/kernel/src/arch/vectors.S
new file mode 100644
index 0000000..175ff95
--- /dev/null
+++ b/kernel/src/arch/vectors.S
@@ -0,0 +1,75 @@
+.section .rodata
+.global el1_vectors
+.p2align 7
+el1_vectors:
+// Current level with SP_EL0
+vec_el1_sp_el0_sync:
+    b .
+.p2align 7
+vec_el1_sp_el0_irq:
+    b .
+.p2align 7
+vec_el1_sp_el0_fiq:
+    b .
+.p2align 7
+vec_el1_sp_el0_serror:
+    b .
+// Current level with SL_ELx, x > 0
+.p2align 7
+vec_el1_sp_elx_sync:
+    sub sp, sp, #192
+    stp x0, x1, [sp, #0]
+    stp x2, x3, [sp, #16]
+    stp x4, x5, [sp, #32]
+    stp x6, x7, [sp, #48]
+    stp x8, x9, [sp, #64]
+    stp x10, x11, [sp, #80]
+    stp x12, x13, [sp, #96]
+    stp x14, x15, [sp, #112]
+    stp x16, x17, [sp, #128]
+    stp x18, x29, [sp, #144]
+    stp x30, xzr, [sp, #160]
+    mrs x0, esr_el1
+    mrs x1, far_el1
+    stp x0, x1, [sp, #176]
+
+    mov x0, sp
+
+    bl exc_handler
+
+.p2align 7
+vec_el1_sp_elx_irq:
+    b .
+.p2align 7
+vec_el1_sp_elx_fiq:
+    b .
+.p2align 7
+vec_el1_sp_elx_serror:
+    b .
+// Lower EL, AArch64
+.p2align 7
+vec_el0_aa64_sync:
+    b .
+.p2align 7
+vec_el0_aa64_irq:
+    b .
+.p2align 7
+vec_el0_aa64_fiq:
+    b .
+.p2align 7
+vec_el0_aa64_serror:
+    b .
+// Lower EL, AArch32
+.p2align 7
+vec_el0_aa32_sync:
+    b .
+.p2align 7
+vec_el0_aa32_irq:
+    b .
+.p2align 7
+vec_el0_aa32_fiq:
+    b .
+.p2align 7
+vec_el0_aa32_serror:
+    b .
+
diff --git a/src/boot/entry.S b/kernel/src/boot/entry.S
similarity index 70%
rename from src/boot/entry.S
rename to kernel/src/boot/entry.S
index e90e405..ba20ba3 100644
--- a/src/boot/entry.S
+++ b/kernel/src/boot/entry.S
@@ -17,9 +17,9 @@
 .set MAIR_EL1_INNER_NC, (4 << 4)
 .set MAIR_EL1_DEVICE_nGRE, 0
 .set MAIR_EL1_DEVICE, 0
-
 .set TCR_EL1_IPS_48, (5 << 32)
 .set TCR_EL1_TG1_4K, (2 << 30)
+.set CPACR_EL1_FPEN_TRAP_NONE, (3 << 20)
 
 .cpu cortex-a57
 .section .text.boot
@@ -134,89 +134,21 @@ upper_half:
     // Shoot off the legs
     msr ttbr0_el1, xzr
 
-    adr x0, el1_vectors
-    msr vbar_el1, x0
+    // Disable trapping for FP instructions
+    mrs x0, cpacr_el1
+    orr x0, x0, CPACR_EL1_FPEN_TRAP_NONE
+    msr cpacr_el1, x0
 
     adr x0, bsp_stack_top
     mov sp, x0
 
+    adr x0, el1_vectors
+    msr vbar_el1, x0
+
     bl kernel_main
 
     b .
 
-.section .rodata
-.p2align 7
-el1_vectors:
-// Current level with SP_EL0
-vec_el1_sp_el0_sync:
-    b .
-.p2align 7
-vec_el1_sp_el0_irq:
-    b .
-.p2align 7
-vec_el1_sp_el0_fiq:
-    b .
-.p2align 7
-vec_el1_sp_el0_serror:
-    b .
-// Current level with SL_ELx, x > 0
-.p2align 7
-vec_el1_sp_elx_sync:
-    sub sp, sp, #192
-    stp x0, x1, [sp, #0]
-    stp x2, x3, [sp, #16]
-    stp x4, x5, [sp, #32]
-    stp x6, x7, [sp, #48]
-    stp x8, x9, [sp, #64]
-    stp x10, x11, [sp, #80]
-    stp x12, x13, [sp, #96]
-    stp x14, x15, [sp, #112]
-    stp x16, x17, [sp, #128]
-    stp x18, x29, [sp, #144]
-    stp x30, xzr, [sp, #160]
-    mrs x0, esr_el1
-    mrs x1, far_el1
-    stp x0, x1, [sp, #176]
-
-    mov x0, sp
-
-    bl exc_handler
-
-.p2align 7
-vec_el1_sp_elx_irq:
-    b .
-.p2align 7
-vec_el1_sp_elx_fiq:
-    b .
-.p2align 7
-vec_el1_sp_elx_serror:
-    b .
-// Lower EL, AArch64
-.p2align 7
-vec_el0_aa64_sync:
-    b .
-.p2align 7
-vec_el0_aa64_irq:
-    b .
-.p2align 7
-vec_el0_aa64_fiq:
-    b .
-.p2align 7
-vec_el0_aa64_serror:
-    b .
-// Lower EL, AArch32
-.p2align 7
-vec_el0_aa32_sync:
-    b .
-.p2align 7
-vec_el0_aa32_irq:
-    b .
-.p2align 7
-vec_el0_aa32_fiq:
-    b .
-.p2align 7
-vec_el0_aa32_serror:
-    b .
 
 .section .bss
 .p2align 4
diff --git a/src/boot/mod.rs b/kernel/src/boot/mod.rs
similarity index 100%
rename from src/boot/mod.rs
rename to kernel/src/boot/mod.rs
diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs
new file mode 100644
index 0000000..b5dd722
--- /dev/null
+++ b/kernel/src/debug.rs
@@ -0,0 +1,28 @@
+use core::fmt;
+
+fn uart_send(ch: u8) {
+    unsafe {
+        core::ptr::write_volatile(0xFFFFFF803F215040 as *mut u32, ch as u32);
+    }
+}
+
+struct Uart;
+
+impl fmt::Write for Uart {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        for ch in s.bytes() {
+            uart_send(ch);
+        }
+        Ok(())
+    }
+}
+
+#[macro_export]
+macro_rules! debug {
+    ($($args:tt)+) => ($crate::debug::debug_fmt(format_args!($($args)+)))
+}
+
+pub fn debug_fmt(args: fmt::Arguments<'_>) {
+    use fmt::Write;
+    write!(Uart {}, "{}", args).unwrap();
+}
diff --git a/kernel/src/main.rs b/kernel/src/main.rs
new file mode 100644
index 0000000..738ce69
--- /dev/null
+++ b/kernel/src/main.rs
@@ -0,0 +1,61 @@
+#![feature(global_asm, llvm_asm, const_panic)]
+#![no_std]
+#![no_main]
+
+#[macro_use]
+pub mod debug;
+
+pub mod arch;
+pub mod boot;
+pub mod mem;
+
+pub use mem::KernelSpace;
+
+use core::fmt::{self, Write};
+
+use address::PhysicalAddress;
+use mem::phys::UsableMemory;
+#[derive(Clone)]
+struct SimpleMemoryIterator<'a> {
+    inner: Option<&'a UsableMemory>,
+}
+impl Iterator for SimpleMemoryIterator<'_> {
+    type Item = UsableMemory;
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(item) = self.inner {
+            self.inner = None;
+            Some(item.clone())
+        } else {
+            None
+        }
+    }
+}
+
+#[no_mangle]
+extern "C" fn kernel_main() -> ! {
+    // TODO determine VC/ARM split
+    let memory = UsableMemory {
+        start: PhysicalAddress::from(0usize),
+        end: PhysicalAddress::from(0x30000000usize),
+    };
+    let iter = SimpleMemoryIterator {
+        inner: Some(&memory),
+    };
+    unsafe {
+        mem::phys::initialize(iter);
+    }
+
+    loop {
+        unsafe {
+            llvm_asm!("wfe");
+        }
+    }
+}
+
+use core::panic::PanicInfo;
+#[panic_handler]
+fn panic_handler(pi: &PanicInfo) -> ! {
+    debug!("PANIC: {:?}\n", pi);
+
+    loop {}
+}
diff --git a/kernel/src/mem/address.rs b/kernel/src/mem/address.rs
new file mode 100644
index 0000000..e69de29
diff --git a/kernel/src/mem/mod.rs b/kernel/src/mem/mod.rs
new file mode 100644
index 0000000..1765a8d
--- /dev/null
+++ b/kernel/src/mem/mod.rs
@@ -0,0 +1,21 @@
+use address::{AddressSpace, PhysicalAddress, TrivialConvert};
+
+pub mod phys;
+
+#[derive(Copy, Clone, PartialEq, PartialOrd)]
+pub struct KernelSpace;
+impl AddressSpace for KernelSpace {
+    const NAME: &'static str = "kernel";
+    const OFFSET: usize = 0xFFFFFF8000000000;
+    const LIMIT: usize = 0xFFFFFF8000000000 + (2 << 30);
+}
+impl TrivialConvert for KernelSpace {}
+
+pub const PAGE_SIZE: usize = 0x1000;
+
+pub fn kernel_end_phys() -> PhysicalAddress {
+    extern "C" {
+        static __kernel_end_phys: u8;
+    }
+    PhysicalAddress::from(unsafe { &__kernel_end_phys } as *const _ as usize)
+}
diff --git a/kernel/src/mem/phys/manager.rs b/kernel/src/mem/phys/manager.rs
new file mode 100644
index 0000000..d0f4c46
--- /dev/null
+++ b/kernel/src/mem/phys/manager.rs
@@ -0,0 +1,62 @@
+use super::{PageInfo, PageUsage};
+use crate::KernelSpace;
+use address::{PhysicalAddress, VirtualAddress};
+use core::mem::{self, MaybeUninit};
+use error::Errno;
+use spin::Mutex;
+
+pub unsafe trait Manager {
+    fn alloc_page(&mut self) -> Result<PhysicalAddress, Errno>;
+    fn alloc_contiguous_pages(&mut self, count: usize) -> Result<PhysicalAddress, Errno>;
+    fn free_page(&mut self, page: PhysicalAddress) -> Result<(), Errno>;
+
+    // TODO status()
+}
+
+pub struct SimpleManager {
+    pages: &'static mut [Mutex<PageInfo>],
+}
+
+impl SimpleManager {
+    pub(super) unsafe fn initialize(at: PhysicalAddress, count: usize) -> Self {
+        let pages: &'static mut [Mutex<PageInfo>] =
+            VirtualAddress::<KernelSpace>::from(at).as_slice_mut(count);
+
+        // Initialize uninit pages
+        for index in 0..count {
+            mem::forget(mem::replace(
+                &mut pages[index],
+                Mutex::new(PageInfo {
+                    refcount: 0,
+                    usage: PageUsage::Reserved,
+                }),
+            ));
+        }
+
+        Self { pages }
+    }
+
+    pub(super) unsafe fn add_page(&mut self, addr: PhysicalAddress) {
+        let mut page = self.pages[addr.page_index()].lock();
+        assert!(page.refcount == 0 && page.usage == PageUsage::Reserved);
+        page.usage = PageUsage::Available;
+
+        // Fill the page with trash
+        let slice: &mut [u8; 4096] = VirtualAddress::<KernelSpace>::from(addr).as_mut();
+        slice.fill(0);
+    }
+}
+
+unsafe impl Manager for SimpleManager {
+    fn alloc_page(&mut self) -> Result<PhysicalAddress, Errno> {
+        todo!()
+    }
+    fn alloc_contiguous_pages(&mut self, _count: usize) -> Result<PhysicalAddress, Errno> {
+        todo!()
+    }
+    fn free_page(&mut self, _page: PhysicalAddress) -> Result<(), Errno> {
+        todo!()
+    }
+}
+
+pub(super) static MANAGER: Mutex<Option<SimpleManager>> = Mutex::new(None);
diff --git a/kernel/src/mem/phys/mod.rs b/kernel/src/mem/phys/mod.rs
new file mode 100644
index 0000000..8fa55eb
--- /dev/null
+++ b/kernel/src/mem/phys/mod.rs
@@ -0,0 +1,118 @@
+use super::PAGE_SIZE;
+use address::PhysicalAddress;
+use core::convert::TryFrom;
+use core::mem::size_of;
+use error::Errno;
+use spin::Mutex;
+
+mod manager;
+use manager::{Manager, SimpleManager, MANAGER};
+mod reserved;
+pub use reserved::ReservedRegion;
+mod pbox;
+pub use pbox::PhysBox;
+
+type ManagerImpl = SimpleManager;
+
+#[derive(PartialEq, Debug)]
+pub enum PageUsage {
+    Reserved,
+    Available,
+    Kernel,
+}
+
+pub struct PageInfo {
+    refcount: usize,
+    usage: PageUsage,
+}
+
+#[derive(Clone)]
+pub struct UsableMemory {
+    pub start: PhysicalAddress,
+    pub end: PhysicalAddress,
+}
+
+const MAX_PAGES: usize = 1024;
+
+pub fn alloc_page() -> Result<PhysicalAddress, Errno> {
+    MANAGER.lock().as_mut().unwrap().alloc_page()
+}
+
+pub fn alloc_contiguous_pages(count: usize) -> Result<PhysicalAddress, Errno> {
+    MANAGER
+        .lock()
+        .as_mut()
+        .unwrap()
+        .alloc_contiguous_pages(count)
+}
+
+pub fn free_page(page: PhysicalAddress) -> Result<(), Errno> {
+    MANAGER.lock().as_mut().unwrap().free_page(page)
+}
+
+fn find_contiguous<T: Iterator<Item = UsableMemory>>(
+    iter: T,
+    count: usize,
+) -> Option<PhysicalAddress> {
+    for region in iter {
+        let mut collected = 0;
+        let mut base_addr = None;
+
+        for addr in (region.start..region.end).step_by(PAGE_SIZE) {
+            if reserved::is_reserved(addr) {
+                collected = 0;
+                base_addr = None;
+                continue;
+            }
+
+            if base_addr.is_none() {
+                base_addr = Some(addr);
+            }
+            collected += 1;
+            if collected == count {
+                return base_addr;
+            }
+        }
+    }
+
+    None
+}
+
+pub unsafe fn initialize<T: Iterator<Item = UsableMemory> + Clone>(iter: T) {
+    // Step 1. Count available memory
+    let mut total_pages = 0usize;
+    for reg in iter.clone() {
+        total_pages +=
+            usize::try_from(PhysicalAddress::diff(reg.start, reg.end)).unwrap() / PAGE_SIZE;
+    }
+    // TODO maybe instead of size_of::<...> use Layout?
+    let need_pages = ((total_pages * size_of::<Mutex<PageInfo>>()) + 0xFFF) / 0x1000;
+
+    reserved::reserve_kernel();
+
+    // Step 2. Allocate memory for page array
+    let pages_base =
+        find_contiguous(iter.clone(), need_pages).expect("Failed to allocate memory for page info");
+
+    reserved::reserve_pages(pages_base, need_pages);
+
+    // Step 3. Initialize the memory manager with available pages
+    let mut manager = ManagerImpl::initialize(pages_base, total_pages);
+
+    let mut usable_pages = 0usize;
+    'l0: for region in iter {
+        for addr in (region.start..region.end).step_by(PAGE_SIZE) {
+            if !reserved::is_reserved(addr) {
+                manager.add_page(addr);
+                usable_pages += 1;
+                if usable_pages == MAX_PAGES {
+                    break 'l0;
+                }
+            }
+        }
+    }
+
+    debug!("{}K of usable physical memory\n", usable_pages * 4);
+
+    *MANAGER.lock() = Some(manager);
+}
diff --git a/kernel/src/mem/phys/pbox.rs b/kernel/src/mem/phys/pbox.rs
new file mode 100644
index 0000000..4dcc8ad
--- /dev/null
+++ b/kernel/src/mem/phys/pbox.rs
@@ -0,0 +1,24 @@
+use address::PhysicalAddress;
+use error::Errno;
+
+pub struct PhysBox {
+    base: PhysicalAddress,
+    count: usize,
+}
+
+impl PhysBox {
+    pub fn new() -> Result<Self, Errno> {
+        Ok(Self {
+            base: super::alloc_page()?,
+            count: 1
+        })
+    }
+}
+
+impl Drop for PhysBox {
+    fn drop(&mut self) {
+        for p in 0..self.count {
+            super::free_page(self.base + p * 0x1000).unwrap();
+        }
+    }
+}
diff --git a/kernel/src/mem/phys/reserved.rs b/kernel/src/mem/phys/reserved.rs
new file mode 100644
index 0000000..9db375d
--- /dev/null
+++ b/kernel/src/mem/phys/reserved.rs
@@ -0,0 +1,79 @@
+use crate::mem::{kernel_end_phys, PAGE_SIZE};
+use address::PhysicalAddress;
+use core::mem::MaybeUninit;
+use core::ptr::null_mut;
+
+pub struct ReservedRegion {
+    pub start: PhysicalAddress,
+    pub end: PhysicalAddress,
+    next: *mut ReservedRegion,
+}
+
+pub struct ReservedRegionIterator {
+    ptr: *mut ReservedRegion,
+}
+
+impl Iterator for ReservedRegionIterator {
+    type Item = &'static mut ReservedRegion;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(item) = unsafe { self.ptr.as_mut() } {
+            self.ptr = item.next;
+            Some(item)
+        } else {
+            None
+        }
+    }
+}
+
+impl ReservedRegion {
+    pub const fn new(start: PhysicalAddress, end: PhysicalAddress) -> ReservedRegion {
+        assert!(start.is_paligned() && end.is_paligned());
+        ReservedRegion {
+            start,
+            end,
+            next: null_mut()
+        }
+    }
+}
+
+static mut RESERVED_REGIONS_HEAD: *mut ReservedRegion = null_mut();
+static mut RESERVED_REGION_KERNEL: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
+static mut RESERVED_REGION_PAGES: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
+
+pub unsafe fn reserve(region: *mut ReservedRegion) {
+    (*region).next = RESERVED_REGIONS_HEAD;
+    RESERVED_REGIONS_HEAD = region;
+}
+
+pub(super) unsafe fn reserve_kernel() {
+    RESERVED_REGION_KERNEL.write(ReservedRegion::new(
+        PhysicalAddress::from(0usize),
+        kernel_end_phys(),
+    ));
+    reserve(RESERVED_REGION_KERNEL.as_mut_ptr());
+}
+
+pub(super) unsafe fn reserve_pages(base: PhysicalAddress, count: usize) {
+    RESERVED_REGION_PAGES.write(ReservedRegion::new(
+        base,
+        base + count * PAGE_SIZE
+    ));
+    reserve(RESERVED_REGION_PAGES.as_mut_ptr());
+}
+
+pub fn is_reserved(page: PhysicalAddress) -> bool {
+    unsafe {
+        let mut iter = RESERVED_REGIONS_HEAD;
+        while !iter.is_null() {
+            let region = &*iter;
+
+            if page >= region.start && page < region.end {
+                return true;
+            }
+
+            iter = region.next;
+        }
+    }
+    false
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..e69de29