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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
use efi;
use log;
let L0: [512]uintptr;
let L1_ident: [512]uintptr;
let L1_kernel: [512]uintptr;
let L2_kernel: [512]uintptr;
let L3_kernel: [512]uintptr;
def PAGESIZE: size = 4096;
def UPAGESIZE: uintptr = 4096;
// Memory attributes
def PT_NORMAL: uintptr = 0 << 2;
def PT_DEVICE: uintptr = 1 << 2;
def PT_TABLE: uintptr = 0b11;
def PT_BLOCK: uintptr = 0b01;
def PT_OFFS: uintptr = 48;
def PT_XN: uintptr = (1 << 54); // execute never
def PT_PXN: uintptr = (1 << 53); // privileged execute never
def PT_CONT: uintptr = (1 << 52); // continuous bit
def PT_NG: uintptr = (1 << 11); // non-global
def PT_AF: uintptr = (1 << 10); // access
def PT_ISM: uintptr = (3 << 8); // inner sharable
def PT_OSM: uintptr = (2 << 8); // outer sharable
def PT_RW: uintptr = (0 << 7); // Read/write
def PT_RO: uintptr = (1 << 7); // Read-only
// Initializes the memory map and returns the physical address of the largest
// area of conventional memory, in lieu of a better memory allocator.
fn init_mmap() uintptr = {
const iter = efi::iter_mmap()!;
let maxphys: uintptr = 0, maxpages = 0u64;
for (true) {
const desc = match (efi::mmap_next(&iter)) {
case let desc: *efi::MEMORY_DESCRIPTOR =>
yield desc;
case void =>
break;
};
if (desc.DescriptorType != efi::MEMORY_TYPE::CONVENTIONAL) {
continue;
};
if (desc.NumberOfPages > maxpages) {
maxphys = desc.PhysicalStart;
maxpages = desc.NumberOfPages;
};
};
assert(maxphys != 0, "No suitable memory area found for kernel loader");
return maxphys;
};
fn init_pagetables() void = {
// 0xFFFF0000xxxxxxxx - 0xFFFF0200xxxxxxxx: identity map
// 0xFFFF8000xxxxxxxx - 0xFFFF8000xxxxxxxx: kernel image
//
// L0[0x00] => L1_ident
// L0[0x80] => L1_kernel
// L1_ident => 1 GiB identity mappings
// L1_kernel[0] => L2_kernel
// L2_kernel[0] => L3_kernel
// L3_kernel[*] => 4 KiB kernel pages
L0[0] = PT_TABLE | &L1_ident: uintptr;
L0[0x80] = PT_TABLE | &L2_kernel: uintptr;
L2_kernel[0] = PT_TABLE | &L3_kernel: uintptr;
for (let i = 0u64; i < len(L1_ident): u64; i += 1) {
L1_ident[i] = PT_BLOCK | (i * 0x40000000): uintptr |
PT_NORMAL | PT_AF | PT_ISM | PT_RW;
};
};
// Maps a physical page into the kernel's virtual address space.
fn kmmap(phys: uintptr, virt: uintptr, flags: uintptr) void = {
return; // TODO
};
def SCTLR_EL1_MMU: u64 = 1 << 0;
def TCR_EL1_DS: u64 = (1 << 59);
def TCR_EL1_TCMA1: u64 = (1 << 58);
def TCR_EL1_TCMA0: u64 = (1 << 57);
def TCR_EL1_E0PD1: u64 = (1 << 56);
def TCR_EL1_E0PD2: u64 = (1 << 55);
def TCR_EL1_NFD0: u64 = (1 << 54);
def TCR_EL1_NFD1: u64 = (1 << 53);
def TCR_EL1_TBID0: u64 = (1 << 52);
def TCR_EL1_TBID1: u64 = (1 << 51);
def TCR_EL1_HWU162: u64 = (1 << 50);
def TCR_EL1_HWU161: u64 = (1 << 49);
def TCR_EL1_HWU160: u64 = (1 << 48);
def TCR_EL1_HWU159: u64 = (1 << 47);
def TCR_EL1_HWU062: u64 = (1 << 46);
def TCR_EL1_HWU061: u64 = (1 << 45);
def TCR_EL1_HWU060: u64 = (1 << 44);
def TCR_EL1_HWU059: u64 = (1 << 43);
def TCR_EL1_HPD1: u64 = (1 << 42);
def TCR_EL1_HPD0: u64 = (1 << 41);
def TCR_EL1_HD: u64 = (1 << 40);
def TCR_EL1_HA: u64 = (1 << 39);
def TCR_EL1_TBI1: u64 = (1 << 38);
def TCR_EL1_TBI0: u64 = (1 << 37);
def TCR_EL1_AS: u64 = (1 << 36);
def TCR_EL1_IPS_MASK: u64 = (0b111 << 32);
def TCR_EL1_IPS_32B_4G: u64 = (0b000 << 32);
def TCR_EL1_IPS_36B_64G: u64 = (0b001 << 32);
def TCR_EL1_IPS_40B_1T: u64 = (0b010 << 32);
def TCR_EL1_IPS_42B_4T: u64 = (0b011 << 32);
def TCR_EL1_IPS_44B_16T: u64 = (0b100 << 32);
def TCR_EL1_IPS_48B_256T: u64 = (0b101 << 32);
def TCR_EL1_IPS_52B_5P: u64 = (0b111 << 32);
def TCR_EL1_TG1_16K: u64 = (0b01 << 30);
def TCR_EL1_TG1_4K: u64 = (0b10 << 30);
def TCR_EL1_TG1_64K: u64 = (0b11 << 30);
def TCR_EL1_SH1_NS: u64 = (0b00 << 28);
def TCR_EL1_SH1_OS: u64 = (0b10 << 28);
def TCR_EL1_SH1_IS: u64 = (0b11 << 28);
def TCR_EL1_ORGN1_NC: u64 = (0b00 << 26);
def TCR_EL1_ORGN1_WB: u64 = (0b01 << 26);
def TCR_EL1_ORGN1_WT: u64 = (0b10 << 26);
def TCR_EL1_ORGN1_NA: u64 = (0b11 << 26);
def TCR_EL1_IRGN1_NC: u64 = (0b00 << 24);
def TCR_EL1_IRGN1_WB: u64 = (0b01 << 24);
def TCR_EL1_IRGN1_WT: u64 = (0b10 << 24);
def TCR_EL1_IRGN1_NA: u64 = (0b11 << 24);
def TCR_EL1_EPD1: u64 = (1 << 23);
def TCR_EL1_A1: u64 = (1 << 22);
def TCR_EL1_T1SZ: u64 = 16;
def TCR_EL1_TG0_4K: u64 = (0b01 << 14);
def TCR_EL1_TG0_64K: u64 = (0b10 << 14);
def TCR_EL1_TG0_16K: u64 = (0b11 << 14);
def TCR_EL1_SH0_NS: u64 = (0b00 << 12);
def TCR_EL1_SH0_OS: u64 = (0b10 << 12);
def TCR_EL1_SH0_IS: u64 = (0b11 << 12);
def TCR_EL1_ORGN0_NC: u64 = (0b00 << 10);
def TCR_EL1_ORGN0_WB: u64 = (0b01 << 10);
def TCR_EL1_ORGN0_WT: u64 = (0b10 << 10);
def TCR_EL1_ORGN0_NA: u64 = (0b11 << 10);
def TCR_EL1_IRGN0_NC: u64 = (0b00 << 8);
def TCR_EL1_IRGN0_WB: u64 = (0b01 << 8);
def TCR_EL1_IRGN0_WT: u64 = (0b10 << 8);
def TCR_EL1_IRGN0_NA: u64 = (0b11 << 8);
def TCR_EL1_EPD0: u64 = (1 << 7);
def TCR_EL1_T0SZ: u64 = 0;
// Initializes the ARM MMU to our desired specifications. This should take place
// *after* EFI boot services have exited because we're going to mess up the MMU
// configuration that it depends on.
fn init_mmu() void = {
// Disable MMU
const sctlr_el1 = rdsctlr_el1();
wrsctlr_el1(sctlr_el1 & ~SCTLR_EL1_MMU);
// Configure MAIR
const mair: u64 =
(0xFF << 0) | // Attr0: Normal memory; IWBWA, OWBWA, NTR
(0x00 << 8); // Attr1: Device memory; nGnRnE, OSH
wrmair_el1(mair);
const ips = rdtcr_el1() & TCR_EL1_IPS_MASK;
log::printfln("old TCR_EL1: {:b}", rdtcr_el1());
const tcr_el1: u64 =
ips | // IPS from EFI
TCR_EL1_TG1_4K | // Higher half: 4K granule size
TCR_EL1_SH1_IS | // Higher half: inner shareable
TCR_EL1_ORGN1_WB | // Higher half: outer write-back
TCR_EL1_IRGN1_WB | // Higher half: inner write-back
TCR_EL1_EPD1 | // Higher half enable
(24 << TCR_EL1_T1SZ) | // Higher half: 3 levels (1024G)
TCR_EL1_TG0_4K | // Lower half: 4K granule size
TCR_EL1_SH0_IS | // Lower half: inner sharable
TCR_EL1_ORGN0_WB | // Lower half: outer write-back
TCR_EL1_IRGN0_WB | // Lower half: inner write-back
TCR_EL1_EPD0 | // Lower half enable
(24 << TCR_EL1_T0SZ); // Lower half: 3 levels (1024G)
wrtcr_el1(tcr_el1);
log::printfln("new TCR_EL1: {:b}", tcr_el1);
// Load page tables
wrttbr0_el1(&L0[0]: uintptr | 1);
wrttbr1_el1(&L0[0]: uintptr | 1);
// Enable MMU
wrsctlr_el1(sctlr_el1 | SCTLR_EL1_MMU);
// Test higher half identity map
//let x = 1337;
//let y = (&x: uintptr | 0xFFFF000000000000: uintptr): *int;
//assert(*y == 1337);
};