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
|
//! The static library that forms the core of the kernel.
#![no_std]
use crate::arch::{sleep_forever, PAGE_SIZE};
use log::{debug, info, warn};
use vernos_alloc_physmem_free_list::FreeListAllocator;
use vernos_device_tree::FlattenedDeviceTree;
use vernos_utils::dbg;
#[cfg(target_os = "none")]
mod panic;
pub mod arch;
/// The entrypoint to the kernel. This should be executed by hart0 alone. It performs some early
/// boot tasks, then wakes up any other harts.
///
/// # Safety
///
/// - The `device_tree` pointer must be a valid pointer into physical memory. See
/// `device_tree::FlattenedDeviceTree::from_ptr` for the precise requirements.
/// - This must be called in supervisor mode with paging and traps disabled, but with all traps
/// delegated to supervisor mode.
/// - Any other harts must not be running concurrently with us. TODO: Define their state.
#[no_mangle]
pub unsafe extern "C" fn hart0_boot(device_tree: *const u8) -> ! {
// Set up the logger.
//
// TODO: This should really be named something better than console.
// console::init();
// Parse the DeviceTree.
let flattened_device_tree =
unsafe { FlattenedDeviceTree::from_ptr(device_tree) }.expect("invalid DeviceTree");
// Find the available physical memory areas and initialize the physical memory
// free-list.
let mut physical_memory_free_list = FreeListAllocator::<PAGE_SIZE>::new();
dbg!(physical_memory_free_list);
/*
flattened_device_tree
.for_each_node(|node| {
if node.is_unit(&["", "memory"]) {
// Get the memory ranges.
let Some(reg) = node.get_reg_usize() else {
warn!("{}reg was not valid", node.name());
return Ok(());
};
for (addr, size) in reg {
physical_memory_free_list.add_range(addr..addr + size);
}
}
Ok(())
})
.unwrap_or_else(|err| void::unreachable(err));
// Log the physical memory we found.
debug!(
"found {} usable regions of physical memory{}",
physical_memory_free_list.len(),
if physical_memory_free_list.is_empty() {
""
} else {
":"
}
);
for region in physical_memory_free_list.drain() {
debug!(
"{:p}..{:p} ({} byte{})",
region.start as *const u8,
region.end as *const u8,
region.len(),
if region.len() == 1 { "" } else { "s" }
)
}
*/
// After this point, everything else is for debugging.
#[cfg(target_arch = "riscv64")]
{
flattened_device_tree
.for_each_node(|node| {
if node.is_unit(&["", "cpus"]) {
if let Some(timebase_frequency) = node.get_prop_u32("timebase-frequency") {
// SAFETY: Other harts are not concurrently running, so they can't be
// concurrently accessing or modifying this.
unsafe {
vernos_driver_riscv_timer::TIMEBASE_FREQUENCY = timebase_frequency;
}
}
}
Ok(())
})
.unwrap_or_else(|err| void::unreachable(err));
arch::interrupts::example_timer();
}
info!("sleeping forever...");
sleep_forever();
}
|