Initial documentation

This commit is contained in:
Mark Poliakov 2021-09-24 13:01:58 +03:00
parent 2b5aa03505
commit e85116c5aa
14 changed files with 160 additions and 42 deletions

View File

@ -6,13 +6,13 @@
const_trait_impl,
const_panic
)]
// #![warn(missing_docs)]
#[cfg(test)]
#[macro_use]
extern crate std;
pub mod virt;
#[deny(missing_docs)]
pub mod phys;
trait Address {}

View File

@ -1,21 +1,24 @@
//! Module for handling physical memory addresses
use crate::{AddressSpace, TrivialConvert, VirtualAddress};
use core::convert::TryFrom;
use core::fmt;
use core::iter::Step;
use core::ops::{Add, AddAssign, Sub, SubAssign};
/// Wrapper struct for representing a physical address
#[repr(transparent)]
#[derive(PartialEq, PartialOrd, Copy, Clone)]
pub struct PhysicalAddress(usize);
// Arithmetic
impl<A: Into<usize>> Add<A> for PhysicalAddress {
impl const Add<usize> for PhysicalAddress {
type Output = Self;
#[inline(always)]
fn add(self, rhs: A) -> Self {
fn add(self, rhs: usize) -> Self {
// Will panic on overflow
Self::from(self.0 + rhs.into())
Self::from(self.0 + rhs)
}
}
impl<A: Into<usize>> AddAssign<A> for PhysicalAddress {
@ -25,7 +28,7 @@ impl<A: Into<usize>> AddAssign<A> for PhysicalAddress {
*self = Self::from(self.0 + rhs.into());
}
}
impl Sub<usize> for PhysicalAddress {
impl const Sub<usize> for PhysicalAddress {
type Output = Self;
#[inline(always)]
@ -41,28 +44,23 @@ impl SubAssign<usize> for PhysicalAddress {
}
// Construction
impl From<usize> for PhysicalAddress {
impl const From<usize> for PhysicalAddress {
fn from(p: usize) -> Self {
Self(p)
}
}
#[cfg(target_pointer_width = "64")]
impl From<u64> for PhysicalAddress {
impl const From<u64> for PhysicalAddress {
fn from(p: u64) -> Self {
Self(p as usize)
}
}
impl PhysicalAddress {
pub const fn new(value: usize) -> Self {
Self(value)
}
pub const fn add(self, value: usize) -> Self {
Self(self.0 + value)
}
/// Computes a signed difference between `start` and `end`
/// addresses. Will panic if the result cannot be represented due to
/// overflow.
#[inline(always)]
pub fn diff(start: PhysicalAddress, end: PhysicalAddress) -> isize {
if end >= start {
@ -72,16 +70,24 @@ impl PhysicalAddress {
}
}
/// Computes a signed difference between `start` and `end`, does not check for
/// overflow condition.
///
/// # Safety
///
/// Does not check for signed integer overflow.
#[inline(always)]
pub fn diff_unchecked(start: PhysicalAddress, end: PhysicalAddress) -> isize {
pub unsafe fn diff_unchecked(start: PhysicalAddress, end: PhysicalAddress) -> isize {
end.0 as isize - start.0 as isize
}
/// Returns `true` if the address is page-aligned
#[inline(always)]
pub const fn is_paligned(self) -> bool {
return self.0 & 0xFFF == 0
self.0 & 0xFFF == 0
}
/// Returns the index of the 4K page containing the address
#[inline(always)]
pub const fn page_index(self) -> usize {
self.0 >> 12
@ -103,7 +109,7 @@ impl const From<PhysicalAddress> for usize {
}
#[cfg(target_pointer_width = "64")]
impl From<PhysicalAddress> for u64 {
impl const From<PhysicalAddress> for u64 {
#[inline(always)]
fn from(p: PhysicalAddress) -> Self {
p.0 as u64

View File

@ -18,18 +18,39 @@ if [ "$PROFILE" = "release" ]; then
CARGO_OPTS="$CARGO_OPTS --release"
fi
cd kernel
cargo build ${CARGO_OPTS}
cd ..
case $ARCH in
aarch64)
${LLVM_BIN}/llvm-objcopy -O binary ${OUT_DIR}/kernel ${OUT_DIR}/kernel.bin
case $1 in
""|build)
CARGO_CMD=build
;;
x86_64)
mkdir -p ${OUT_DIR}/cdrom/boot/grub
cp etc/x86_64-none.grub ${OUT_DIR}/cdrom/boot/grub/grub.cfg
cp ${OUT_DIR}/kernel ${OUT_DIR}/cdrom/boot/kernel.elf
grub-mkrescue -o ${OUT_DIR}/cdrom.iso ${OUT_DIR}/cdrom
clean)
CARGO_CMD=clean
;;
clippy)
CARGO_CMD=clippy
;;
doc)
shift
if [ x$1 = xopen ]; then
CARGO_OPTS="$CARGO_OPTS --open"
fi
CARGO_CMD=doc
;;
esac
cd kernel
cargo ${CARGO_CMD} ${CARGO_OPTS}
cd ..
if [ ${CARGO_CMD} = "build" ]; then
case $ARCH in
aarch64)
${LLVM_BIN}/llvm-objcopy -O binary ${OUT_DIR}/kernel ${OUT_DIR}/kernel.bin
;;
x86_64)
mkdir -p ${OUT_DIR}/cdrom/boot/grub
cp etc/x86_64-none.grub ${OUT_DIR}/cdrom/boot/grub/grub.cfg
cp ${OUT_DIR}/kernel ${OUT_DIR}/cdrom/boot/kernel.elf
grub-mkrescue -o ${OUT_DIR}/cdrom.iso ${OUT_DIR}/cdrom
;;
esac
fi

View File

@ -1,4 +1,4 @@
use cortex_a::asm;
//! aarch64 common boot logic
#[no_mangle]
fn __aa64_bsp_main() {
@ -11,7 +11,7 @@ fn __aa64_bsp_main() {
}
loop {
let ch = unsafe { machine::console().lock().recv(true).unwrap() };
let ch = machine::console().lock().recv(true).unwrap();
debugln!("{:#04x} = '{}'!", ch, ch as char);
}
}

View File

@ -1,8 +1,11 @@
//! QEMU virt machine
use crate::dev::serial::{pl011::Pl011, SerialDevice};
use crate::sync::Spin;
pub const UART0_BASE: usize = 0x09000000;
const UART0_BASE: usize = 0x09000000;
/// Returns primary console for this machine
#[inline]
pub fn console() -> &'static Spin<impl SerialDevice> {
&UART0

View File

@ -1,3 +1,5 @@
//! aarch64 architecture implementation
pub mod boot;
cfg_if! {

View File

@ -1,3 +1,14 @@
//! Architecture-specific detail module
//!
//! Contains two module aliases, which may or may not point
//! the same architecture module:
//!
//! * [platform] - architecture details (e.g. aarch64)
//! * [machine] - particular machine implementation (e.g. bcm2837)
//!
//! Modules visible in the documentation will depend on
//! build target platform.
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
pub mod aarch64;
@ -11,12 +22,18 @@ cfg_if! {
use core::ops::Deref;
use core::marker::PhantomData;
/// Wrapper for setting up memory-mapped registers and IO
pub struct MemoryIo<T> {
base: usize,
_pd: PhantomData<fn() -> T>,
}
impl<T> MemoryIo<T> {
/// Constructs a new instance of MMIO region.
///
/// # Safety
///
/// Does not perform `base` validation.
pub const unsafe fn new(base: usize) -> Self {
Self {
base,

View File

@ -1,3 +1,8 @@
//! Debug output module.
//!
//! The module provides [debug!] and [debugln!] macros
//! which can be used in similar way to print! and
//! println! from std.
use crate::dev::serial::SerialDevice;
use crate::sync::Spin;
use core::fmt;
@ -10,28 +15,31 @@ impl<T: SerialDevice> fmt::Write for SerialOutput<T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let mut lock = self.inner.lock();
for &byte in s.as_bytes() {
unsafe {
// TODO check for errors
drop(lock.send(byte));
}
// TODO check for errors
lock.send(byte).ok();
}
Ok(())
}
}
/// Writes a debug message to debug output
#[macro_export]
macro_rules! debug {
($($it:tt)+) => ($crate::debug::_debug(format_args!($($it)+)))
}
/// Writes a debug message, followed by a newline, to debug output
///
/// See [debug!]
#[macro_export]
macro_rules! debugln {
($($it:tt)+) => (debug!("{}\n", format_args!($($it)+)))
}
#[doc(hidden)]
pub fn _debug(args: fmt::Arguments) {
use crate::arch::machine;
use fmt::Write;
drop(SerialOutput { inner: machine::console() }.write_fmt(args));
SerialOutput { inner: machine::console() }.write_fmt(args).ok();
}

View File

@ -1,9 +1,19 @@
//! Module for device interfaces and drivers
use error::Errno;
pub mod serial;
/// Generic device trait
pub trait Device {
/// Returns device type/driver name
fn name() -> &'static str;
/// Performs device initialization logic.
///
/// # Safety
///
/// Marked unsafe as it may cause direct hardware-specific side-effects.
/// Additionally, may be called twice with undefined results.
unsafe fn enable(&mut self) -> Result<(), Errno>;
}

View File

@ -1,9 +1,17 @@
//! Module for serial device drivers
use crate::dev::Device;
use error::Errno;
pub mod pl011;
/// Generic interface for serial devices
pub trait SerialDevice: Device {
unsafe fn send(&mut self, byte: u8) -> Result<(), Errno>;
unsafe fn recv(&mut self, blocking: bool) -> Result<u8, Errno>;
/// Transmits (blocking) a byte through the serial device
fn send(&mut self, byte: u8) -> Result<(), Errno>;
/// Receives a byte through the serial interface.
///
/// If `blocking` is `false` and there's no data in device's queue,
/// will return [Errno::WouldBlock].
fn recv(&mut self, blocking: bool) -> Result<u8, Errno>;
}

View File

@ -1,3 +1,5 @@
//! PL011 - ARM PrimeCell UART implementation
use crate::arch::MemoryIo;
use crate::dev::{serial::SerialDevice, Device};
use error::Errno;
@ -7,6 +9,7 @@ use tock_registers::{
registers::{ReadOnly, ReadWrite, WriteOnly},
};
/// Device struct for PL011
#[repr(transparent)]
pub struct Pl011 {
regs: MemoryIo<Regs>,
@ -14,36 +17,52 @@ pub struct Pl011 {
register_bitfields! {
u32,
/// Flag register
FR [
/// Transmit FIFO full
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy
BUSY OFFSET(3) NUMBITS(1) [],
],
/// Control register
CR [
/// Enable UART receiver
RXE OFFSET(9) NUMBITS(1) [],
/// Enable UART transmitter
TXE OFFSET(8) NUMBITS(1) [],
/// Enable UART
UARTEN OFFSET(0) NUMBITS(1) [],
],
/// Interrupt clear register
ICR [
/// Writing this to ICR clears all IRQs
ALL OFFSET(0) NUMBITS(11) []
]
}
register_structs! {
/// PL011 registers
#[allow(non_snake_case)]
Regs {
/// Data register
(0x00 => DR: ReadWrite<u32>),
(0x04 => _res1),
/// Flag register
(0x18 => FR: ReadOnly<u32, FR::Register>),
/// Line control register
(0x2C => LCR_H: ReadWrite<u32>),
/// Control register
(0x30 => CR: ReadWrite<u32, CR::Register>),
/// Interrupt clear register
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
(0x04 => @END),
}
}
impl SerialDevice for Pl011 {
unsafe fn send(&mut self, byte: u8) -> Result<(), Errno> {
fn send(&mut self, byte: u8) -> Result<(), Errno> {
while self.regs.FR.matches_all(FR::TXFF::SET) {
core::hint::spin_loop();
}
@ -51,7 +70,7 @@ impl SerialDevice for Pl011 {
Ok(())
}
unsafe fn recv(&mut self, blocking: bool) -> Result<u8, Errno> {
fn recv(&mut self, blocking: bool) -> Result<u8, Errno> {
if self.regs.FR.matches_all(FR::RXFE::SET) {
if !blocking {
return Err(Errno::WouldBlock);
@ -82,6 +101,11 @@ impl Device for Pl011 {
}
impl Pl011 {
/// Constructs an instance of PL011 device.
///
/// # Safety
///
/// Does not perform `base` validation.
pub const unsafe fn new(base: usize) -> Self {
Self {
regs: MemoryIo::new(base),

View File

@ -8,6 +8,7 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate cfg_if;
@ -16,6 +17,7 @@ pub mod debug;
pub mod arch;
pub mod dev;
pub mod mem;
#[deny(missing_docs)]
pub mod sync;
#[panic_handler]

View File

@ -1,3 +1,11 @@
//! Memory management and functions module
/// Implements the rust language dependency for memcpy(3p) function.
///
/// # Safety
///
/// Unsafe: writes to arbitrary memory locations, performs no pointer
/// validation.
#[no_mangle]
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *mut u8, mut len: usize) -> *mut u8 {
while len != 0 {

View File

@ -1,22 +1,31 @@
//! Synchronization facilities module
use core::ops::{Deref, DerefMut};
use core::cell::UnsafeCell;
/// Dummy lock implementation, does not do any locking.
///
/// Only safe to use before I implement context switching or
/// interrupts are enabled.
#[repr(transparent)]
pub struct NullLock<T: ?Sized> {
value: UnsafeCell<T>
}
/// Dummy lock guard for [NullLock].
#[repr(transparent)]
pub struct NullLockGuard<'a, T: ?Sized> {
value: &'a mut T
}
impl<T> NullLock<T> {
/// Constructs a new instance of the lock, wrapping `value`
#[inline(always)]
pub const fn new(value: T) -> Self {
Self { value: UnsafeCell::new(value) }
}
/// Returns [NullLockGuard] for this lock
#[inline(always)]
pub fn lock(&self) -> NullLockGuard<T> {
NullLockGuard { value: unsafe { &mut *self.value.get() } }