1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use core::alloc::Layout;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};

/// A physical address, with an in-memory representation equivalent to a regular pointer to a value
/// of type `T`. Uses the kernel's 1:1 physical memory mapping for accesses via [`Self::ptr()`] and
/// [`Self::ptr_mut()`].
///
/// As with a regular pointer, this type confers no guarantees of validity or alignment.
///
/// The physical address may:
/// - be null
/// - **be unmapped in the kernel's 1:1 physical memory mapping** (an issue unique to physical
///   addresses)
/// - point to uninitialised memory
/// - alias an existing immutable or mutable reference
/// - be misaligned for a value of type `T`
/// - lack any other guarantee as described in [`core::ptr`] or [the nomicon]
///
/// [the nomicon]: https://doc.rust-lang.org/nightly/nomicon/
#[derive(Debug)]
#[repr(transparent)]
pub struct PhysicalAddress<T> {
    addr: usize,
    phantom: PhantomData<T>,
}

impl<T> PhysicalAddress<T> {
    /// The base of the kernel's 1:1 physical memory mapping.
    const PHYS_BASE: usize = 0xffff_0000_0000_0000;

    pub fn from_addr(addr: usize) -> Self {
        Self {
            addr,
            phantom: PhantomData,
        }
    }

    /// Returns the physical address.
    pub fn addr(self) -> usize {
        self.addr
    }

    /// Returns a regular pointer using the kernel's 1:1 physical memory mapping.
    pub fn ptr(self) -> *const T {
        (Self::PHYS_BASE + self.addr) as *const _
    }

    /// Returns a regular, mutable pointer using the kernel's 1:1 physical memory mapping.
    pub fn ptr_mut(self) -> *mut T {
        (Self::PHYS_BASE + self.addr) as *mut _
    }

    /// Casts to a physical address of another type.
    pub fn cast<U>(self) -> PhysicalAddress<U> {
        PhysicalAddress {
            addr: self.addr,
            phantom: PhantomData,
        }
    }
}

// a pointer is Clone even if T isn't Clone (so we can't just `#[derive(Clone)]`)
impl<T> Clone for PhysicalAddress<T> {
    fn clone(&self) -> Self {
        self.cast()
    }
}

// a pointer is Copy even if T isn't Copy (so we can't just `#[derive(Copy)]`)
impl<T> Copy for PhysicalAddress<T> {}

/// An owned page.
#[derive(Debug)]
#[repr(transparent)]
pub struct PageBox<T>(PhysicalAddress<T>);

impl<T> PageBox<T> {
    /// Allocates a new page and places `val` into it.
    pub fn new(val: T) -> Self {
        let layout = Layout::for_value(&val);
        let pa = PageAllocator.alloc(layout).cast::<T>();

        unsafe { pa.ptr_mut().write_volatile(val) };

        Self(pa)
    }

    pub fn addr(&self) -> PhysicalAddress<T> {
        self.0
    }

    pub fn leak(self) -> PhysicalAddress<T> {
        self.0
    }
}

impl<T> Drop for PageBox<T> {
    fn drop(&mut self) {
        unsafe { self.0.ptr_mut().drop_in_place() }
    }
}

impl<T> Deref for PageBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.0.ptr() }
    }
}

impl<T> DerefMut for PageBox<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.0.ptr_mut() }
    }
}

// TODO: move this somewhere better, and implement a better allocator that actually tracks
// allocations
static mut ALLOC_BASE: usize = 0x4000_0000 + 0x10_0000;

struct PageAllocator;

impl PageAllocator {
    const PAGE_SIZE: usize = 0x1000;

    /// Allocates a page in physical memory and returns the physical address of the page.
    fn alloc(&self, layout: Layout) -> PhysicalAddress<[u8; Self::PAGE_SIZE]> {
        // we don't support zero-sized allocations
        // TODO: should we support zero-sized allocations?
        assert!(layout.size() > 0);
        // this is a single page, so we can't support an allocation larger than a page
        assert!(layout.size() <= Self::PAGE_SIZE);
        // Layout::align() is guaranteed to be a power of two, so this ensures that the layout's
        // alignment is compatible with page alignment
        assert!(layout.align() <= Self::PAGE_SIZE);

        unsafe {
            let pa = PhysicalAddress::from_addr(ALLOC_BASE);
            ALLOC_BASE += Self::PAGE_SIZE;
            pa
        }
    }
}