mirror of
https://github.com/sgmarz/osblog.git
synced 2024-11-24 02:16:19 +04:00
Added chapter 2
This commit is contained in:
parent
1402b8c8bd
commit
10c493738e
5
risc_v/ch2/.cargo/config
Normal file
5
risc_v/ch2/.cargo/config
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[build]
|
||||||
|
target = "riscv64gc-unknown-none-elf"
|
||||||
|
|
||||||
|
[target.riscv64gc-unknown-none-elf]
|
||||||
|
linker = "riscv64-unknown-linux-gnu-gcc"
|
12
risc_v/ch2/Cargo.toml
Normal file
12
risc_v/ch2/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "sos"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Stephen Marz <stephen.marz@utk.edu>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
38
risc_v/ch2/Makefile
Normal file
38
risc_v/ch2/Makefile
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#####
|
||||||
|
## BUILD
|
||||||
|
#####
|
||||||
|
CC=riscv64-unknown-linux-gnu-g++
|
||||||
|
CFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g -std=c++17
|
||||||
|
CFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions
|
||||||
|
CFLAGS+=-march=rv64gc -mabi=lp64
|
||||||
|
INCLUDES=
|
||||||
|
LINKER_SCRIPT=-Tsrc/lds/virt.lds
|
||||||
|
TYPE=debug
|
||||||
|
RUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)
|
||||||
|
LIBS=-L$(RUST_TARGET)
|
||||||
|
SOURCES_ASM=$(wildcard src/asm/*.S)
|
||||||
|
LIB=-lsos -lgcc
|
||||||
|
OUT=os.elf
|
||||||
|
|
||||||
|
#####
|
||||||
|
## QEMU
|
||||||
|
#####
|
||||||
|
QEMU=qemu-system-riscv64
|
||||||
|
MACH=virt
|
||||||
|
CPU=rv64
|
||||||
|
CPUS=4
|
||||||
|
MEM=128M
|
||||||
|
DRIVE=hdd.dsk
|
||||||
|
|
||||||
|
all:
|
||||||
|
cargo build
|
||||||
|
$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)
|
||||||
|
|
||||||
|
run: all
|
||||||
|
$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) -nographic -serial mon:stdio -bios none -kernel $(OUT) -drive if=none,format=raw,file=$(DRIVE),id=foo -device virtio-blk-device,scsi=off,drive=foo
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
cargo clean
|
||||||
|
rm -f $(OUT)
|
3
risc_v/ch2/make_hdd.sh
Executable file
3
risc_v/ch2/make_hdd.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
dd if=/dev/zero of=hdd.dsk bs=1M count=32
|
57
risc_v/ch2/src/asm/boot.S
Executable file
57
risc_v/ch2/src/asm/boot.S
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
# boot.S
|
||||||
|
# bootloader for SoS
|
||||||
|
# Stephen Marz
|
||||||
|
# 8 February 2019
|
||||||
|
.option norvc
|
||||||
|
.section .data
|
||||||
|
.section .text.init
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
# Any hardware threads (hart) that are not bootstrapping
|
||||||
|
# need to wait for an IPI
|
||||||
|
csrr t0, mhartid
|
||||||
|
bnez t0, 3f
|
||||||
|
# SATP should be zero, but let's make sure
|
||||||
|
csrw satp, zero
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
la gp, _global_pointer
|
||||||
|
.option pop
|
||||||
|
# The BSS section is expected to be zero
|
||||||
|
la a0, _bss_start
|
||||||
|
la a1, _bss_end
|
||||||
|
bgeu a0, a1, 2f
|
||||||
|
1:
|
||||||
|
sd zero, (a0)
|
||||||
|
addi a0, a0, 8
|
||||||
|
bltu a0, a1, 1b
|
||||||
|
2:
|
||||||
|
# Control registers, set the stack, mstatus, mepc,
|
||||||
|
# and mtvec to return to the main function.
|
||||||
|
# li t5, 0xffff;
|
||||||
|
# csrw medeleg, t5
|
||||||
|
# csrw mideleg, t5
|
||||||
|
la sp, _stack
|
||||||
|
# We use mret here so that the mstatus register
|
||||||
|
# is properly updated.
|
||||||
|
li t0, (0b11 << 11) | (1 << 7) | (1 << 3)
|
||||||
|
csrw mstatus, t0
|
||||||
|
la t1, kmain
|
||||||
|
csrw mepc, t1
|
||||||
|
la t2, asm_trap_vector
|
||||||
|
csrw mtvec, t2
|
||||||
|
li t3, (1 << 3) | (1 << 7) | (1 << 11)
|
||||||
|
csrw mie, t3
|
||||||
|
la ra, 4f
|
||||||
|
mret
|
||||||
|
3:
|
||||||
|
|
||||||
|
# Parked harts go here. We need to set these
|
||||||
|
# to only awaken if it receives a software interrupt,
|
||||||
|
# which we're going to call the SIPI (Software Intra-Processor Interrupt).
|
||||||
|
# We only use these to run user-space programs, although this may
|
||||||
|
# change.
|
||||||
|
4:
|
||||||
|
wfi
|
||||||
|
j 4b
|
||||||
|
|
9
risc_v/ch2/src/asm/trap.S
Normal file
9
risc_v/ch2/src/asm/trap.S
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# trap.S
|
||||||
|
# In the future our trap vector will go here.
|
||||||
|
|
||||||
|
.global asm_trap_vector
|
||||||
|
# This will be our trap vector when we start
|
||||||
|
# handling interrupts.
|
||||||
|
asm_trap_vector:
|
||||||
|
mret
|
||||||
|
|
49
risc_v/ch2/src/lds/virt.lds
Normal file
49
risc_v/ch2/src/lds/virt.lds
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
|
||||||
|
ENTRY( _start )
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
|
||||||
|
}
|
||||||
|
|
||||||
|
PHDRS
|
||||||
|
{
|
||||||
|
text PT_LOAD;
|
||||||
|
data PT_LOAD;
|
||||||
|
bss PT_LOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.text : {
|
||||||
|
PROVIDE(_text_start = .);
|
||||||
|
*(.text.init) *(.text .text.*)
|
||||||
|
PROVIDE(_text_end = .);
|
||||||
|
} >ram AT>ram :text
|
||||||
|
PROVIDE(_global_pointer = .);
|
||||||
|
.rodata : {
|
||||||
|
PROVIDE(_rodata_start = .);
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
PROVIDE(_rodata_end = .);
|
||||||
|
} >ram AT>ram :text
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
. = ALIGN(4096);
|
||||||
|
PROVIDE(_data_start = .);
|
||||||
|
*(.sdata .sdata.*) *(.data .data.*)
|
||||||
|
PROVIDE(_data_end = .);
|
||||||
|
} >ram AT>ram :data
|
||||||
|
|
||||||
|
.bss :{
|
||||||
|
PROVIDE(_bss_start = .);
|
||||||
|
*(.sbss .sbss.*) *(.bss .bss.*)
|
||||||
|
PROVIDE(_bss_end = .);
|
||||||
|
} >ram AT>ram :bss
|
||||||
|
|
||||||
|
PROVIDE(_memory_start = ORIGIN(ram));
|
||||||
|
PROVIDE(_stack = _bss_end + 0x80000);
|
||||||
|
PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
|
||||||
|
PROVIDE(_heap_start = _stack);
|
||||||
|
PROVIDE(_heap_size = _memory_end - _stack);
|
||||||
|
}
|
92
risc_v/ch2/src/lib.rs
Executable file
92
risc_v/ch2/src/lib.rs
Executable file
@ -0,0 +1,92 @@
|
|||||||
|
// Steve Operating System
|
||||||
|
// Stephen Marz
|
||||||
|
// 21 Sep 2019
|
||||||
|
#![no_std]
|
||||||
|
#![feature(panic_info_message,asm)]
|
||||||
|
|
||||||
|
// ///////////////////////////////////
|
||||||
|
// / 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
|
||||||
|
// ///////////////////////////////////
|
||||||
|
|
||||||
|
// ///////////////////////////////////
|
||||||
|
// / ENTRY POINT
|
||||||
|
// ///////////////////////////////////
|
||||||
|
#[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.
|
||||||
|
uart::Uart::new(0x1000_0000).init();
|
||||||
|
|
||||||
|
// Now test println! macro!
|
||||||
|
println!("This is my operating system!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///////////////////////////////////
|
||||||
|
// / RUST MODULES
|
||||||
|
// ///////////////////////////////////
|
||||||
|
|
||||||
|
pub mod uart;
|
117
risc_v/ch2/src/uart.rs
Executable file
117
risc_v/ch2/src/uart.rs
Executable file
@ -0,0 +1,117 @@
|
|||||||
|
// uart.rs
|
||||||
|
// UART routines and driver
|
||||||
|
|
||||||
|
use core::convert::TryInto;
|
||||||
|
use core::fmt::Write;
|
||||||
|
use core::fmt::Error;
|
||||||
|
|
||||||
|
pub struct Uart {
|
||||||
|
base_address: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Uart {
|
||||||
|
fn write_str(&mut self, out: &str) -> Result<(), Error> {
|
||||||
|
for c in out.as_bytes() {
|
||||||
|
self.put(*c);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Uart {
|
||||||
|
pub fn new(base_address: usize) -> Self {
|
||||||
|
Uart {
|
||||||
|
base_address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
let ptr = self.base_address as *mut u8;
|
||||||
|
unsafe {
|
||||||
|
// First, set the word length and stop bits, which
|
||||||
|
// are bits 0, 1, and 2 of the line control register (LCR)
|
||||||
|
// which is at base_address + 3
|
||||||
|
// We can easily write the value 7 here or 0b111, but I'm
|
||||||
|
// extending it so that it is clear we're setting two individual
|
||||||
|
// fields
|
||||||
|
// Word len Stop bits
|
||||||
|
// ~~~~~~~~ ~~~~~~~~~
|
||||||
|
ptr.add(3).write_volatile((0b11 << 0) | (1 << 2));
|
||||||
|
|
||||||
|
// Now, enable the FIFO, which is bit index 0 of the FIFO
|
||||||
|
// control register (FCR at offset 2).
|
||||||
|
// Again, we can just write 1 here, but when we use left shift,
|
||||||
|
// it's easier to see that we're trying to write bit index #0.
|
||||||
|
ptr.add(2).write_volatile(1 << 0);
|
||||||
|
|
||||||
|
// Enable receiver buffer interrupts, which is at bit index
|
||||||
|
// 0 of the interrupt enable register (IER at offset 1).
|
||||||
|
ptr.add(1).write_volatile(1 << 0);
|
||||||
|
|
||||||
|
// If we cared about the divisor, the code below would set the divisor
|
||||||
|
// from a global clock rate of 22.729 MHz (22,729,000 cycles per second)
|
||||||
|
// to a signaling rate of 2400 (BAUD). We usually have much faster signalling
|
||||||
|
// rates nowadays, but this demonstrates what the divisor actually does.
|
||||||
|
// The formula given in the NS16500A specification for calculating the divisor
|
||||||
|
// is:
|
||||||
|
// divisor = ceil( (clock_hz) / (baud_sps x 16) )
|
||||||
|
// So, we substitute our values and get:
|
||||||
|
// divisor = ceil( 22_729_000 / (2400 x 16) )
|
||||||
|
// divisor = ceil( 22_729_000 / 38_400 )
|
||||||
|
// divisor = ceil( 591.901 ) = 592
|
||||||
|
|
||||||
|
// The divisor register is two bytes (16 bits), so we need to split the value
|
||||||
|
// 592 into two bytes. Typically, we would calculate this based on measuring
|
||||||
|
// the clock rate, but again, for our purposes [qemu], this doesn't really do
|
||||||
|
// anything.
|
||||||
|
// let divisor: u16 = 592;
|
||||||
|
// let divisor_least: u8 = (divisor & 0xff).try_into().unwrap();
|
||||||
|
// let divisor_most: u8 = (divisor >> 8).try_into().unwrap();
|
||||||
|
|
||||||
|
// Notice that the divisor register DLL (divisor latch least) and DLM (divisor latch most)
|
||||||
|
// have the same base address as the receiver/transmitter and the interrupt enable register.
|
||||||
|
// To change what the base address points to, we open the "divisor latch" by writing 1 into
|
||||||
|
// the Divisor Latch Access Bit (DLAB), which is bit index 7 of the Line Control Register (LCR)
|
||||||
|
// which is at base_address + 3.
|
||||||
|
// ptr.add(3).write_volatile(1 << 7);
|
||||||
|
|
||||||
|
// Now, base addresses 0 and 1 point to DLL and DLM, respectively.
|
||||||
|
// Put the lower 8 bits of the divisor into DLL
|
||||||
|
// ptr.add(0).write_volatile(divisor_least);
|
||||||
|
// ptr.add(1).write_volatile(divisor_most);
|
||||||
|
|
||||||
|
// Now that we've written the divisor, we never have to touch this again. In hardware, this
|
||||||
|
// will divide the global clock (22.729 MHz) into one suitable for 2,400 signals per second.
|
||||||
|
// So, to once again get access to the RBR/THR/IER registers, we need to close the DLAB bit
|
||||||
|
// by clearing it to 0.
|
||||||
|
// let lcr = ptr.add(3).read_volatile();
|
||||||
|
// ptr.add(3).write_volatile(lcr & !(1 << 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put(&mut self, c: u8) {
|
||||||
|
let ptr = self.base_address as *mut u8;
|
||||||
|
unsafe {
|
||||||
|
while ptr.add(5).read_volatile() & (1 << 6) == 0 {
|
||||||
|
// Wait for the transmitter to drain.
|
||||||
|
}
|
||||||
|
// If we get here, the transmitter is empty, so transmit
|
||||||
|
// our stuff!
|
||||||
|
ptr.add(0).write_volatile(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self) -> Option<u8> {
|
||||||
|
let ptr = self.base_address as *mut u8;
|
||||||
|
unsafe {
|
||||||
|
if ptr.add(5).read_volatile() & 1 == 0 {
|
||||||
|
// The DR bit is 0, meaning no data
|
||||||
|
None
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The DR bit is 1, meaning data!
|
||||||
|
Some(ptr.add(0).read_volatile())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user