mirror of
https://github.com/sgmarz/osblog.git
synced 2024-11-24 10:26:20 +04:00
337 lines
8.4 KiB
Rust
Executable File
337 lines
8.4 KiB
Rust
Executable File
// Steve Operating System
|
|
// Stephen Marz
|
|
// 21 Sep 2019
|
|
#![no_std]
|
|
#![feature(panic_info_message,
|
|
asm,
|
|
allocator_api,
|
|
alloc_error_handler,
|
|
alloc_prelude,
|
|
const_raw_ptr_to_usize_cast)]
|
|
|
|
#[macro_use]
|
|
extern crate alloc;
|
|
// This is experimental and requires alloc_prelude as a feature
|
|
use alloc::prelude::v1::*;
|
|
|
|
// ///////////////////////////////////
|
|
// / RUST MACROS
|
|
// ///////////////////////////////////
|
|
#[macro_export]
|
|
macro_rules! print
|
|
{
|
|
($($args:tt)+) => ({
|
|
use core::fmt::Write;
|
|
let _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);
|
|
});
|
|
}
|
|
#[macro_export]
|
|
macro_rules! println
|
|
{
|
|
() => ({
|
|
print!("\r\n")
|
|
});
|
|
($fmt:expr) => ({
|
|
print!(concat!($fmt, "\r\n"))
|
|
});
|
|
($fmt:expr, $($args:tt)+) => ({
|
|
print!(concat!($fmt, "\r\n"), $($args)+)
|
|
});
|
|
}
|
|
|
|
// ///////////////////////////////////
|
|
// / LANGUAGE STRUCTURES / FUNCTIONS
|
|
// ///////////////////////////////////
|
|
#[no_mangle]
|
|
extern "C" fn eh_personality() {}
|
|
|
|
#[panic_handler]
|
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
|
print!("Aborting: ");
|
|
if let Some(p) = info.location() {
|
|
println!(
|
|
"line {}, file {}: {}",
|
|
p.line(),
|
|
p.file(),
|
|
info.message().unwrap()
|
|
);
|
|
}
|
|
else {
|
|
println!("no information available.");
|
|
}
|
|
abort();
|
|
}
|
|
#[no_mangle]
|
|
extern "C" fn abort() -> ! {
|
|
loop {
|
|
unsafe {
|
|
asm!("wfi"::::"volatile");
|
|
}
|
|
}
|
|
}
|
|
|
|
// ///////////////////////////////////
|
|
// / CONSTANTS
|
|
// ///////////////////////////////////
|
|
// const STR_Y: &str = "\x1b[38;2;79;221;13m✓\x1b[m";
|
|
// const STR_N: &str = "\x1b[38;2;221;41;13m✘\x1b[m";
|
|
|
|
extern "C" {
|
|
static TEXT_START: usize;
|
|
static TEXT_END: usize;
|
|
static DATA_START: usize;
|
|
static DATA_END: usize;
|
|
static RODATA_START: usize;
|
|
static RODATA_END: usize;
|
|
static BSS_START: usize;
|
|
static BSS_END: usize;
|
|
static KERNEL_STACK_START: usize;
|
|
static KERNEL_STACK_END: usize;
|
|
static mut KERNEL_TABLE: usize;
|
|
}
|
|
/// Identity map range
|
|
/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE
|
|
/// This assumes that start <= end
|
|
pub fn id_map_range(root: &mut page::Table,
|
|
start: usize,
|
|
end: usize,
|
|
bits: i64)
|
|
{
|
|
let start_aligned = start & !(page::PAGE_SIZE - 1);
|
|
let num_pages = (page::align_val(end, 12)
|
|
- start_aligned)
|
|
/ page::PAGE_SIZE;
|
|
for i in 0..num_pages {
|
|
let m = start_aligned + (i << 12);
|
|
page::map(root, m, m, bits);
|
|
}
|
|
}
|
|
// ///////////////////////////////////
|
|
// / ENTRY POINT
|
|
// ///////////////////////////////////
|
|
#[no_mangle]
|
|
extern "C" fn kinit() -> usize {
|
|
// We created kinit, which runs in super-duper mode
|
|
// 3 called "machine mode".
|
|
// The job of kinit() is to get us into supervisor mode
|
|
// as soon as possible.
|
|
// Interrupts are disabled for the duration of kinit()
|
|
uart::Uart::new(0x1000_0000).init();
|
|
page::init();
|
|
kmem::init();
|
|
|
|
// Map heap allocations
|
|
let root_ptr = kmem::get_page_table();
|
|
let root_u = root_ptr as usize;
|
|
let mut root = unsafe { root_ptr.as_mut().unwrap() };
|
|
let kheap_head = kmem::get_head() as usize;
|
|
let total_pages = kmem::get_num_allocations();
|
|
id_map_range(
|
|
&mut root,
|
|
kheap_head,
|
|
kheap_head + (total_pages << 12),
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
unsafe {
|
|
// Map executable section
|
|
id_map_range(
|
|
&mut root,
|
|
TEXT_START,
|
|
TEXT_END,
|
|
page::EntryBits::ReadExecute.val(),
|
|
);
|
|
// Map rodata section
|
|
// We put the ROdata section into the text section, so they can
|
|
// potentially overlap however, we only care that it's read
|
|
// only.
|
|
id_map_range(
|
|
&mut root,
|
|
RODATA_START,
|
|
RODATA_END,
|
|
page::EntryBits::ReadExecute.val(),
|
|
);
|
|
// Map data section
|
|
id_map_range(
|
|
&mut root,
|
|
DATA_START,
|
|
DATA_END,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
// Map bss section
|
|
id_map_range(
|
|
&mut root,
|
|
BSS_START,
|
|
BSS_END,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
// Map kernel stack
|
|
id_map_range(
|
|
&mut root,
|
|
KERNEL_STACK_START,
|
|
KERNEL_STACK_END,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
}
|
|
|
|
// UART
|
|
page::map(
|
|
&mut root,
|
|
0x1000_0000,
|
|
0x1000_0000,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
|
|
// CLINT
|
|
// -> MSIP
|
|
page::map(
|
|
&mut root,
|
|
0x0200_0000,
|
|
0x0200_0000,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
// -> MTIMECMP
|
|
page::map(
|
|
&mut root,
|
|
0x0200_b000,
|
|
0x0200_b000,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
// -> MTIME
|
|
page::map(
|
|
&mut root,
|
|
0x0200_c000,
|
|
0x0200_c000,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
// PLIC
|
|
id_map_range(
|
|
&mut root,
|
|
0x0c00_0000,
|
|
0x0c00_2000,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
id_map_range(
|
|
&mut root,
|
|
0x0c20_0000,
|
|
0x0c20_8000,
|
|
page::EntryBits::ReadWrite.val(),
|
|
);
|
|
page::print_page_allocations();
|
|
// When we return from here, we'll go back to boot.S and switch into
|
|
// supervisor mode We will return the SATP register to be written when
|
|
// we return. root_u is the root page table's address. When stored into
|
|
// the SATP register, this is divided by 4 KiB (right shift by 12 bits).
|
|
// We enable the MMU by setting mode 8. Bits 63, 62, 61, 60 determine
|
|
// the mode. 0 = Bare (no translation)
|
|
// 8 = Sv39
|
|
// 9 = Sv48
|
|
unsafe {
|
|
KERNEL_TABLE = root_u;
|
|
}
|
|
(root_u >> 12) | (8 << 60)
|
|
}
|
|
|
|
#[no_mangle]
|
|
extern "C" fn kmain() {
|
|
// Main should initialize all sub-systems and get
|
|
// ready to start scheduling. The last thing this
|
|
// should do is start the timer.
|
|
|
|
// Let's try using our newly minted UART by initializing it first.
|
|
// The UART is sitting at MMIO address 0x1000_0000, so for testing
|
|
// now, lets connect to it and see if we can initialize it and write
|
|
// to it.
|
|
let mut my_uart = uart::Uart::new(0x1000_0000);
|
|
|
|
println!();
|
|
println!();
|
|
println!("This is my operating system!");
|
|
println!(
|
|
"I'm so awesome. If you start typing something, I'll show \
|
|
you what you typed!"
|
|
);
|
|
// Create a new scope so that we can test the global allocator and
|
|
// deallocator
|
|
{
|
|
// We have the global allocator, so let's see if that works!
|
|
let k: Box<u32> = Box::new(100);
|
|
println!("Boxed value = {}", *k);
|
|
kmem::print_table();
|
|
// The following comes from the Rust documentation:
|
|
// some bytes, in a vector
|
|
let sparkle_heart = vec![240, 159, 146, 150];
|
|
// We know these bytes are valid, so we'll use `unwrap()`.
|
|
let sparkle_heart = String::from_utf8(sparkle_heart).unwrap();
|
|
println!("String = {}", sparkle_heart);
|
|
}
|
|
// Now see if we can read stuff:
|
|
// Usually we can use #[test] modules in Rust, but it would convolute
|
|
// the task at hand. So, we'll just add testing snippets.
|
|
loop {
|
|
if let Some(c) = my_uart.get() {
|
|
match c {
|
|
8 => {
|
|
// This is a backspace, so we
|
|
// essentially have to write a space and
|
|
// backup again:
|
|
print!("{} {}", 8 as char, 8 as char);
|
|
},
|
|
10 | 13 => {
|
|
// Newline or carriage-return
|
|
println!();
|
|
},
|
|
0x1b => {
|
|
// Those familiar with ANSI escape
|
|
// sequences knows that this is one of
|
|
// them. The next thing we should get is
|
|
// the left bracket [
|
|
// These are multi-byte sequences, so we
|
|
// can take a chance and get from UART
|
|
// ourselves. Later, we'll button this
|
|
// up.
|
|
if let Some(next_byte) = my_uart.get() {
|
|
if next_byte == 91 {
|
|
// This is a right
|
|
// bracket! We're on our
|
|
// way!
|
|
if let Some(b) =
|
|
my_uart.get()
|
|
{
|
|
match b as char
|
|
{
|
|
'A' => {
|
|
println!("That's the up arrow!");
|
|
},
|
|
'B' => {
|
|
println!("That's the down arrow!");
|
|
},
|
|
'C' => {
|
|
println!("That's the right arrow!");
|
|
},
|
|
'D' => {
|
|
println!("That's the left arrow!");
|
|
},
|
|
_ => {
|
|
println!("That's something else.....");
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_ => {
|
|
print!("{}", c as char);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ///////////////////////////////////
|
|
// / RUST MODULES
|
|
// ///////////////////////////////////
|
|
|
|
pub mod kmem;
|
|
pub mod page;
|
|
pub mod uart;
|