Renamed rust library to theseus. Replaced uartgetc() with rust implementation.

feat/start
hheik 2024-04-30 17:41:56 +03:00
parent 8d44d9c964
commit 1ee5be8cd6
10 changed files with 225 additions and 28 deletions

View File

@ -1,8 +1,5 @@
K=kernel
U=user
R=rust
RTARGET = riscv64gc-unknown-none-elf
OBJS = \
$K/entry.o \
@ -33,8 +30,11 @@ OBJS = \
$K/plic.o \
$K/virtio_disk.o
R = theseus
RTARGET = riscv64gc-unknown-none-elf
RLIBS = \
$R/foo/target/$(RTARGET)/release/libfoo.a
$R/target/$(RTARGET)/debug/lib$(R).a
# riscv64-unknown-elf- or riscv64-linux-gnu-
# perhaps in /opt/riscv/bin
@ -145,7 +145,7 @@ fs.img: mkfs/mkfs README $(UPROGS)
-include kernel/*.d user/*.d
rustlibs:
cargo build -r --manifest-path $R/foo/Cargo.toml
cargo build --manifest-path $R/Cargo.toml
clean:
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
@ -154,7 +154,7 @@ clean:
mkfs/mkfs .gdbinit \
$U/usys.S \
$(UPROGS)
cargo clean --manifest-path $R/foo/Cargo.toml
cargo clean --manifest-path $R/Cargo.toml
# try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000)

View File

@ -3,7 +3,6 @@
#include "memlayout.h"
#include "riscv.h"
#include "defs.h"
#include "rust.h"
volatile static int started = 0;
@ -15,8 +14,6 @@ void main() {
printf("\n");
printf("xv6 kernel is booting\n");
printf("\n");
printf("rust library call: add(1, 2) = %d\n", add(1, 2));
printf("\n");
kinit(); // physical page allocator
kvminit(); // create kernel page table
kvminithart(); // turn on paging

View File

@ -147,17 +147,6 @@ void uartstart() {
}
}
// read one input character from the UART.
// return -1 if none is waiting.
int uartgetc(void) {
if (ReadReg(LSR) & 0x01) {
// input data is ready.
return ReadReg(RHR);
} else {
return -1;
}
}
// handle a uart interrupt, raised because input has
// arrived, or the uart is ready for more output, or
// both. called from devintr().

View File

@ -3,5 +3,5 @@
version = 3
[[package]]
name = "foo"
name = "theseus"
version = "0.1.0"

View File

@ -1,5 +1,5 @@
[package]
name = "foo"
name = "theseus"
version = "0.1.0"
edition = "2021"
@ -9,6 +9,4 @@ crate-type = [ "staticlib" ]
[profile.release]
panic = "abort"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,10 +1,9 @@
#![no_std]
#![crate_type = "staticlib"]
#[no_mangle]
pub extern "C" fn add(left: i32, right: i32) -> i32 {
left + right
}
pub mod memlayout;
pub mod riscv;
pub mod uart;
#[panic_handler]
#[no_mangle]

94
theseus/src/memlayout.rs Normal file
View File

@ -0,0 +1,94 @@
use crate::riscv::{MAXVA, PGSIZE};
// Physical memory layout
// qemu -machine virt is set up like this,
// based on qemu's hw/riscv/virt.c:
//
// 00001000 -- boot ROM, provided by qemu
// 02000000 -- CLINT
// 0C000000 -- PLIC
// 10000000 -- uart0
// 10001000 -- virtio disk
// 80000000 -- boot ROM jumps here in machine mode
// -kernel loads the kernel here
// unused RAM after 80000000.
// the kernel uses physical memory thus:
// 80000000 -- entry.S, then kernel text and data
// end -- start of kernel page allocation area
// PHYSTOP -- end RAM used by the kernel
// qemu puts UART registers here in physical memory.
pub const UART0: u64 = 0x10000000;
pub const UART0_IRQ: i32 = 10;
// virtio mmio interface
pub const VIRTIO0: i32 = 0x10001000;
pub const VIRTIO0_IRQ: i32 = 1;
// core local interruptor (CLINT), which contains the timer.
pub const CLINT: i64 = 0x2000000;
// const CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8 * (hartid));
#[inline]
pub fn CLINT_MTIMECMP(hartid: i32) -> i64 {
CLINT + 0x4000 + 8 * hartid as i64
}
pub const CLINT_MTIME: i64 = CLINT + 0xBFF8; // cycles since boot.
// qemu puts platform-level interrupt controller (PLIC) here.
pub const PLIC: i64 = 0x0c000000;
pub const PLIC_PRIORITY: i64 = PLIC + 0x0;
pub const PLIC_PENDING: i64 = PLIC + 0x1000;
#[inline]
pub fn PLIC_MENABLE(hart: i32) -> i64 {
PLIC + 0x2000 + hart as i64 * 0x100
}
#[inline]
pub fn PLIC_SENABLE(hart: i32) -> i64 {
PLIC + 0x2080 + hart as i64 * 0x100
}
#[inline]
pub fn PLIC_MPRIORITY(hart: i32) -> i64 {
PLIC + 0x200000 + hart as i64 * 0x2000
}
#[inline]
pub fn PLIC_SPRIORITY(hart: i32) -> i64 {
PLIC + 0x201000 + hart as i64 * 0x2000
}
#[inline]
pub fn PLIC_MCLAIM(hart: i32) -> i64 {
PLIC + 0x200004 + hart as i64 * 0x2000
}
#[inline]
pub fn PLIC_SCLAIM(hart: i32) -> i64 {
PLIC + 0x201004 + hart as i64 * 0x2000
}
// the kernel expects there to be RAM
// for use by the kernel and user pages
// from physical address 0x80000000 to PHYSTOP.
pub const KERNBASE: i64 = 0x80000000;
pub const PHYSTOP: i64 = KERNBASE + 128 * 1024 * 1024;
// map the trampoline page to the highest address,
// in both user and kernel space.
pub const TRAMPOLINE: u64 = MAXVA - PGSIZE;
// map kernel stacks beneath the trampoline,
// each surrounded by invalid guard pages.
#[inline]
pub fn KSTACK(p: i32) -> u64 {
TRAMPOLINE - (p as u64 + 1) * 2 * PGSIZE
}
// User memory layout.
// Address zero first:
// text
// original data and bss
// fixed-size stack
// expandable heap
// ...
// TRAPFRAME (p->trapframe, used by the trampoline)
// TRAMPOLINE (the same page as in the kernel)
pub const TRAPFRAME: u64 = TRAMPOLINE - PGSIZE;

50
theseus/src/riscv.rs Normal file
View File

@ -0,0 +1,50 @@
pub const PGSIZE: u64 = 4096; // bytes per page
pub const PGSHIFT: i32 = 12; // bits of offset within a page
#[inline]
pub fn PGROUNDUP(sz: u64) -> u64 {
(sz + PGSIZE as u64 - 1) & !(PGSIZE as u64 - 1)
}
#[inline]
pub fn PGROUNDDOWN(a: u64) -> u64 {
a & !(PGSIZE as u64 - 1)
}
pub const PTE_V: i64 = 1 << 0; // valid
pub const PTE_R: i64 = 1 << 1;
pub const PTE_W: i64 = 1 << 2;
pub const PTE_X: i64 = 1 << 3;
pub const PTE_U: i64 = 1 << 4; // user can access
// shift a physical address to the right place for a PTE.
#[inline]
pub fn PA2PTE(pa: u64) -> u64 {
(pa >> 12) << 10
}
#[inline]
pub fn PTE2PA(pte: u64) -> u64 {
(pte >> 10) << 12
}
#[inline]
pub fn PTE_FLAGS(pte: u64) -> u64 {
pte & 0x3FF
}
// extract the three 9-bit page table indices from a virtual address.
const PXMASK: i32 = 0x1FF; // 9 bits
#[inline]
pub fn PXSHIFT(level: u64) -> u64 {
PGSHIFT as u64 + (9 * level)
}
#[inline]
pub fn PX(level: u64, va: u64) -> u64 {
(va >> PXSHIFT(level)) & PXMASK as u64
}
// one beyond the highest possible virtual address.
// MAXVA is actually one bit less than the max allowed by
// Sv39, to avoid having to sign-extend virtual addresses
// that have the high bit set.
pub const MAXVA: u64 = 1 << (9 + 9 + 9 + 12 - 1);

70
theseus/src/uart.rs Normal file
View File

@ -0,0 +1,70 @@
use crate::memlayout::UART0;
// the UART control registers are memory-mapped
// at address UART0. this macro returns the
// address of one of the registers.
#[inline]
pub fn Reg(reg: u8) -> *mut u8 {
(UART0 + reg as u64) as *mut u8
}
// the UART control registers.
// some have different meanings for
// read vs write.
// see http://byterunner.com/16550.html
const RHR: u8 = 0; // receive holding register (for input bytes)
const THR: u8 = 0; // transmit holding register (for output bytes)
const IER: u8 = 1; // interrupt enable register
const IER_RX_ENABLE: u8 = 1 << 0;
const IER_TX_ENABLE: u8 = 1 << 1;
const FCR: u8 = 2; // FIFO control register
const FCR_FIFO_ENABLE: u8 = 1 << 0;
const FCR_FIFO_CLEAR: u8 = 3 << 1; // clear the content of the two FIFOs
const ISR: u8 = 2; // interrupt status register
const LCR: u8 = 3; // line control register
const LCR_EIGHT_BITS: u8 = 3 << 0;
const LCR_BAUD_LATCH: u8 = 1 << 7; // special mode to set baud rate
const LSR: u8 = 5; // line status register
const LSR_RX_READY: u8 = 1 << 0; // input is waiting to be read from RHR
const LSR_TX_IDLE: u8 = 1 << 5; // THR can accept another character to send
#[inline]
pub fn ReadReg(reg: u8) -> u8 {
unsafe { *Reg(reg) }
}
#[inline]
pub fn WriteReg(reg: u8, v: u8) {
unsafe { *Reg(reg) = v }
}
// the transmit output buffer.
// static mut uart_tx_lock: crate::spinlock::SpinLock = crate::spinlock::SpinLock { locked: false };
const UART_TX_BUF_SIZE: usize = 32;
static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE] = [0; UART_TX_BUF_SIZE];
static mut uart_tx_w: u64 = 0; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE]
static mut uart_tx_r: u64 = 0; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]
static mut panicked: i32 = 0;
// #[no_mangle]
// pub extern "C" fn uartinit() {}
// #[no_mangle]
// pub extern "C" fn uartintr() {}
// #[no_mangle]
// pub extern "C" fn uartputc(c: i32) {}
// #[no_mangle]
// pub extern "C" fn uartputc_sync(c: i32) {}
#[no_mangle]
// TODO: Convert to Option<u8>
pub unsafe extern "C" fn uartgetc() -> i32 {
if ReadReg(LSR) & 0x01 != 0 {
ReadReg(RHR) as i32
} else {
-1
}
}