summaryrefslogtreecommitdiff
path: root/crates/kernel/src/lib.rs
blob: 6e09d2bd3c7ebded5f168348ac4de6c8b6cc74b3 (plain)
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
//! The static library that forms the core of the kernel.
#![no_std]

use crate::arch::{sleep_forever, PAGE_SIZE, PAGE_SIZE_BITS};
use core::ptr::NonNull;
use log::{debug, info, warn};
use vernos_alloc_buddy::BuddyAllocator;
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;
pub mod logger;

/// 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.
    logger::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();
    let mut physical_memory_region_count = 0;
    flattened_device_tree
        .for_each_memory_range::<_, PAGE_SIZE>(|addrs| {
            dbg!(&addrs);
            let len_bytes = addrs.end - addrs.start;
            assert!(addrs.start.trailing_zeros() as usize >= PAGE_SIZE_BITS);
            assert!(len_bytes.trailing_zeros() as usize >= PAGE_SIZE_BITS);
            // UNWRAP: for_each_memory_range avoids returning the zero address.
            let addr = NonNull::new(addrs.start as *mut [u8; PAGE_SIZE]).unwrap();
            let len_pages = len_bytes >> PAGE_SIZE_BITS;

            physical_memory_free_list.add(addr, len_pages);
            physical_memory_region_count += 1;
            Ok(())
        })
        .unwrap_or_else(|err| void::unreachable(err));

    // Log the physical memory we found.
    debug!(
        "found {} usable regions of physical memory{}",
        physical_memory_region_count,
        if physical_memory_region_count == 0 {
            ""
        } else {
            ":"
        }
    );
    for (addr, len_pages) in physical_memory_free_list.iter() {
        debug!(
            "{:p}..{:p} ({} bytes)",
            addr.as_ptr(),
            addr.as_ptr().wrapping_add(len_pages),
            len_pages << PAGE_SIZE_BITS,
        )
    }

    // Initialize the buddy allocator.
    let alloc_buddy =
        BuddyAllocator::<PAGE_SIZE, PAGE_SIZE_BITS, 19>::new(physical_memory_free_list)
            .expect("failed to configure the buddy allocator");
    dbg!(alloc_buddy.debug_free_lists());

    // 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();
}