From b2fe1799fd6dc0466a1a982fd454b7d9c7039cd6 Mon Sep 17 00:00:00 2001 From: ticki Date: Sun, 14 Aug 2016 18:42:32 +0200 Subject: [PATCH] Rename the src directory to `kernel`. --- arch/mod.rs | 7 +++ arch/x86_64/gdt.rs | 31 ++++++++++++ arch/x86_64/idt.rs | 75 ++++++++++++++++++++++++++++ arch/x86_64/io/io.rs | 61 +++++++++++++++++++++++ arch/x86_64/io/mmio.rs | 31 ++++++++++++ arch/x86_64/io/mod.rs | 9 ++++ arch/x86_64/io/pio.rs | 83 +++++++++++++++++++++++++++++++ arch/x86_64/irq.rs | 10 ++++ arch/x86_64/main.rs | 78 +++++++++++++++++++++++++++++ arch/x86_64/mem.rs | 69 ++++++++++++++++++++++++++ arch/x86_64/mod.rs | 25 ++++++++++ arch/x86_64/serial.rs | 39 +++++++++++++++ arch/x86_64/tss.rs | 22 +++++++++ lib.rs | 108 +++++++++++++++++++++++++++++++++++++++++ scheme/mod.rs | 12 +++++ 15 files changed, 660 insertions(+) create mode 100644 arch/mod.rs create mode 100644 arch/x86_64/gdt.rs create mode 100644 arch/x86_64/idt.rs create mode 100644 arch/x86_64/io/io.rs create mode 100644 arch/x86_64/io/mmio.rs create mode 100644 arch/x86_64/io/mod.rs create mode 100644 arch/x86_64/io/pio.rs create mode 100644 arch/x86_64/irq.rs create mode 100644 arch/x86_64/main.rs create mode 100644 arch/x86_64/mem.rs create mode 100644 arch/x86_64/mod.rs create mode 100644 arch/x86_64/serial.rs create mode 100644 arch/x86_64/tss.rs create mode 100644 lib.rs create mode 100644 scheme/mod.rs diff --git a/arch/mod.rs b/arch/mod.rs new file mode 100644 index 0000000..924dc1a --- /dev/null +++ b/arch/mod.rs @@ -0,0 +1,7 @@ +//! Architecture specific items + +#[cfg(target_arch = "x86_64")] +pub use self::x86_64::*; + +#[cfg(target_arch = "x86_64")] +pub mod x86_64; diff --git a/arch/x86_64/gdt.rs b/arch/x86_64/gdt.rs new file mode 100644 index 0000000..27648bb --- /dev/null +++ b/arch/x86_64/gdt.rs @@ -0,0 +1,31 @@ +pub const GDT_NULL: usize = 0; +pub const GDT_KERNEL_CODE: usize = 1; +pub const GDT_KERNEL_DATA: usize = 2; +pub const GDT_USER_CODE: usize = 3; +pub const GDT_USER_DATA: usize = 4; +pub const GDT_USER_TLS: usize = 5; +pub const GDT_TSS: usize = 6; + +#[repr(packed)] +pub struct GdtDescriptor { + pub size: u16, + pub ptr: u64 +} + +#[repr(packed)] +pub struct GdtEntry { + pub limitl: u16, + pub basel: u16, + pub basem: u8, + pub attribute: u8, + pub flags_limith: u8, + pub baseh: u8 +} + +impl GdtEntry { + pub fn set_base(&mut self, base: usize) { + self.basel = base as u16; + self.basem = (base >> 16) as u8; + self.baseh = (base >> 24) as u8; + } +} diff --git a/arch/x86_64/idt.rs b/arch/x86_64/idt.rs new file mode 100644 index 0000000..7cc422a --- /dev/null +++ b/arch/x86_64/idt.rs @@ -0,0 +1,75 @@ +use core::mem; + +pub static mut IDTR: IdtDescriptor = IdtDescriptor { + size: 0, + offset: 0 +}; + +pub static mut IDT: [IdtEntry; 256] = [IdtEntry::new(); 256]; + +bitflags! { + pub flags IdtFlags: u8 { + const IDT_PRESENT = 1 << 7, + const IDT_RING_0 = 0 << 5, + const IDT_RING_1 = 1 << 5, + const IDT_RING_2 = 2 << 5, + const IDT_RING_3 = 3 << 5, + const IDT_SS = 1 << 4, + const IDT_INTERRUPT = 0xE, + const IDT_TRAP = 0xF, + } +} + +#[repr(packed)] +pub struct IdtDescriptor { + size: u16, + offset: u64 +} + +impl IdtDescriptor { + pub fn set_slice(&mut self, slice: &'static [IdtEntry]) { + self.size = (slice.len() * mem::size_of::() - 1) as u16; + self.offset = slice.as_ptr() as u64; + } + + pub unsafe fn load(&self) { + asm!("lidt [rax]" : : "{rax}"(self as *const _ as usize) : : "intel", "volatile"); + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(packed)] +pub struct IdtEntry { + offsetl: u16, + selector: u16, + zero: u8, + attribute: u8, + offsetm: u16, + offseth: u32, + zero2: u32 +} + +impl IdtEntry { + pub const fn new() -> IdtEntry { + IdtEntry { + offsetl: 0, + selector: 0, + zero: 0, + attribute: 0, + offsetm: 0, + offseth: 0, + zero2: 0 + } + } + + pub fn set_flags(&mut self, flags: IdtFlags) { + self.attribute = flags.bits; + } + + pub fn set_offset(&mut self, selector: u16, base: usize) { + self.selector = selector; + self.offsetl = base as u16; + self.offsetm = (base >> 16) as u16; + self.offseth = (base >> 32) as u32; + } +} diff --git a/arch/x86_64/io/io.rs b/arch/x86_64/io/io.rs new file mode 100644 index 0000000..a2d5fc6 --- /dev/null +++ b/arch/x86_64/io/io.rs @@ -0,0 +1,61 @@ +use core::cmp::PartialEq; +use core::ops::{BitAnd, BitOr, Not}; + +pub trait Io { + type Value: Copy + PartialEq + BitAnd + BitOr + Not; + + fn read(&self) -> Self::Value; + fn write(&mut self, value: Self::Value); + + fn readf(&self, flags: Self::Value) -> bool { + (self.read() & flags) as Self::Value == flags + } + + fn writef(&mut self, flags: Self::Value, value: bool) { + let tmp: Self::Value = match value { + true => self.read() | flags, + false => self.read() & !flags, + }; + self.write(tmp); + } +} + +pub struct ReadOnly { + inner: I +} + +impl ReadOnly { + pub fn new(inner: I) -> ReadOnly { + ReadOnly { + inner: inner + } + } + + pub fn read(&self) -> I::Value { + self.inner.read() + } + + pub fn readf(&self, flags: I::Value) -> bool { + self.inner.readf(flags) + } +} + +pub struct WriteOnly { + inner: I +} + +impl WriteOnly { + pub fn new(inner: I) -> WriteOnly { + WriteOnly { + inner: inner + } + } + + pub fn write(&mut self, value: I::Value) { + self.inner.write(value) + } + + pub fn writef(&mut self, flags: I::Value, value: bool) { + self.inner.writef(flags, value) + } +} diff --git a/arch/x86_64/io/mmio.rs b/arch/x86_64/io/mmio.rs new file mode 100644 index 0000000..1a1d199 --- /dev/null +++ b/arch/x86_64/io/mmio.rs @@ -0,0 +1,31 @@ +use core::intrinsics::{volatile_load, volatile_store}; +use core::mem::uninitialized; +use core::ops::{BitAnd, BitOr, Not}; + +use super::io::Io; + +#[repr(packed)] +pub struct Mmio { + value: T, +} + +impl Mmio { + /// Create a new Mmio without initializing + pub fn new() -> Self { + Mmio { + value: unsafe { uninitialized() } + } + } +} + +impl Io for Mmio where T: Copy + PartialEq + BitAnd + BitOr + Not { + type Value = T; + + fn read(&self) -> T { + unsafe { volatile_load(&self.value) } + } + + fn write(&mut self, value: T) { + unsafe { volatile_store(&mut self.value, value) }; + } +} diff --git a/arch/x86_64/io/mod.rs b/arch/x86_64/io/mod.rs new file mode 100644 index 0000000..86a1c34 --- /dev/null +++ b/arch/x86_64/io/mod.rs @@ -0,0 +1,9 @@ +/// I/O functions + +pub use self::io::*; +pub use self::mmio::*; +pub use self::pio::*; + +mod io; +mod mmio; +mod pio; diff --git a/arch/x86_64/io/pio.rs b/arch/x86_64/io/pio.rs new file mode 100644 index 0000000..1b10428 --- /dev/null +++ b/arch/x86_64/io/pio.rs @@ -0,0 +1,83 @@ +use core::marker::PhantomData; + +use super::io::Io; + +/// Generic PIO +#[derive(Copy, Clone)] +pub struct Pio { + port: u16, + value: PhantomData, +} + +impl Pio { + /// Create a PIO from a given port + pub fn new(port: u16) -> Self { + Pio:: { + port: port, + value: PhantomData, + } + } +} + +/// Read/Write for byte PIO +impl Io for Pio { + type Value = u8; + + /// Read + fn read(&self) -> u8 { + let value: u8; + unsafe { + asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + fn write(&mut self, value: u8) { + unsafe { + asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for word PIO +impl Io for Pio { + type Value = u16; + + /// Read + fn read(&self) -> u16 { + let value: u16; + unsafe { + asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + fn write(&mut self, value: u16) { + unsafe { + asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for doubleword PIO +impl Io for Pio { + type Value = u32; + + /// Read + fn read(&self) -> u32 { + let value: u32; + unsafe { + asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + fn write(&mut self, value: u32) { + unsafe { + asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} diff --git a/arch/x86_64/irq.rs b/arch/x86_64/irq.rs new file mode 100644 index 0000000..bbceaa7 --- /dev/null +++ b/arch/x86_64/irq.rs @@ -0,0 +1,10 @@ +//! # IRQ handling +//! +//! This module defines IRQ handling functions. These functions should all be #[naked], +//! unsafe, extern, and end in `iretq` + +/// Interupt Request handler. +#[naked] +pub unsafe extern fn irq() { + +} diff --git a/arch/x86_64/main.rs b/arch/x86_64/main.rs new file mode 100644 index 0000000..50748b1 --- /dev/null +++ b/arch/x86_64/main.rs @@ -0,0 +1,78 @@ +/// This function is where the kernel sets up IRQ handlers +/// It is increcibly unsafe, and should be minimal in nature +/// It must create the IDT with the correct entries, those entries are +/// defined in other files inside of the `arch` module + +use super::idt::{IDTR, IDT, IDT_PRESENT, IDT_RING_0, IDT_INTERRUPT}; +use super::mem::memset; + +extern { + /// The starting byte of the text (code) data segment. + static mut __text_start: u8; + /// The ending byte of the text (code) data segment. + static mut __text_end: u8; + /// The starting byte of the _.rodata_ (read-only data) segment. + static mut __rodata_start: u8; + /// The ending byte of the _.rodata_ (read-only data) segment. + static mut __rodata_end: u8; + /// The starting byte of the _.data_ segment. + static mut __data_start: u8; + /// The ending byte of the _.data_ segment. + static mut __data_end: u8; + /// The starting byte of the _.bss_ (uninitialized data) segment. + static mut __bss_start: u8; + /// The ending byte of the _.bss_ (uninitialized data) segment. + static mut __bss_end: u8; +} + +/// Test of zero values in BSS. +static BSS_TEST_ZERO: usize = 0; +/// Test of non-zero values in BSS. +static BSS_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF; + +#[no_mangle] +pub unsafe extern "C" fn kmain() { + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + // Zero BSS, this initializes statics that are set to 0 + { + let start_ptr = &mut __bss_start as *mut u8; + let end_ptr = & __bss_end as *const u8 as usize; + + if start_ptr as usize <= end_ptr { + let size = end_ptr - start_ptr as usize; + memset(start_ptr, 0, size); + } + + //debug_assert_eq!(BSS_TEST_ZERO, 0); + //debug_assert_eq!(BSS_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF); + } + + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + //Set up IDT + for entry in IDT.iter_mut() { + entry.set_flags(IDT_PRESENT | IDT_RING_0 | IDT_INTERRUPT); + entry.set_offset(8, blank as usize); + } + IDTR.set_slice(&IDT); + IDTR.load(); + + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + asm!("int 0xFF" : : : : "intel", "volatile"); + + asm!("xchg bx, bx" : : : : "intel", "volatile"); + + print!("TEST\n"); + + loop{ + asm!("hlt" : : : : "intel", "volatile"); + } +} + +#[naked] +pub unsafe extern "C" fn blank() { + asm!("xchg bx, bx" : : : : "intel", "volatile"); + asm!("iretq" : : : : "intel", "volatile"); +} diff --git a/arch/x86_64/mem.rs b/arch/x86_64/mem.rs new file mode 100644 index 0000000..8e1678c --- /dev/null +++ b/arch/x86_64/mem.rs @@ -0,0 +1,69 @@ +/// Copy memory. +/// +/// Copy N bytes of memory from one location to another. +#[no_mangle] +pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *dest.offset(i as isize) = *src.offset(i as isize); + i += 1; + } + + dest +} + +/// Copy (possibly overlapping) memory. +/// +/// Copy N bytes of memory from src to dest. The memory areas may overlap. +#[no_mangle] +pub unsafe extern fn memmove(dest: *mut u8, src: *const u8, + n: usize) -> *mut u8 { + if src < dest as *const u8 { + let mut i = n; + while i != 0 { + i -= 1; + *dest.offset(i as isize) = *src.offset(i as isize); + } + } else { + let mut i = 0; + while i < n { + *dest.offset(i as isize) = *src.offset(i as isize); + i += 1; + } + } + + dest +} + +/// Set memory. +/// +/// Fill a block of memory with a specified value. +#[no_mangle] +pub unsafe extern fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 { + let mut i = 0; + while i < n { + *s.offset(i as isize) = c as u8; + i += 1; + } + + s +} + +/// Compare memory. +/// +/// Compare two blocks of memory. +#[no_mangle] +pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let mut i = 0; + + while i < n { + let a = *s1.offset(i as isize); + let b = *s2.offset(i as isize); + if a != b { + return a as i32 - b as i32 + } + i += 1; + } + + 0 +} diff --git a/arch/x86_64/mod.rs b/arch/x86_64/mod.rs new file mode 100644 index 0000000..f9239d2 --- /dev/null +++ b/arch/x86_64/mod.rs @@ -0,0 +1,25 @@ +//! X86_64 architecture primitives. + +/// Global descriptor table. +pub mod gdt; + +/// Interrupt descriptor table. +pub mod idt; + +/// IO handling. +pub mod io; + +/// IRQ handling. +pub mod irq; + +/// Initialization and main function. +pub mod main; + +/// Core memory routines. +pub mod mem; + +/// Serial driver and `print!` support. +pub mod serial; + +/// Task state segment. +pub mod tss; diff --git a/arch/x86_64/serial.rs b/arch/x86_64/serial.rs new file mode 100644 index 0000000..dcc9abf --- /dev/null +++ b/arch/x86_64/serial.rs @@ -0,0 +1,39 @@ +use core::fmt; +use super::io::{Io, Pio}; + +pub struct SerialConsole { + status: Pio, + data: Pio +} + +impl SerialConsole { + pub fn new() -> SerialConsole { + SerialConsole { + status: Pio::new(0x3F8 + 5), + data: Pio::new(0x3F8) + } + } + + pub fn write(&mut self, bytes: &[u8]) { + for byte in bytes.iter() { + while !self.status.readf(0x20) {} + self.data.write(*byte); + + if *byte == 8 { + while !self.status.readf(0x20) {} + self.data.write(0x20); + + while !self.status.readf(0x20) {} + self.data.write(8); + } + } + } +} + +impl fmt::Write for SerialConsole { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + self.write(s.as_bytes()); + + Ok(()) + } +} diff --git a/arch/x86_64/tss.rs b/arch/x86_64/tss.rs new file mode 100644 index 0000000..389de39 --- /dev/null +++ b/arch/x86_64/tss.rs @@ -0,0 +1,22 @@ +/// The Task State Segment. +#[repr(C, packed)] +#[derive(Debug, Default, Clone)] +pub struct Tss { + /// Reserved. + pub _reserved1: u32, + /// The stack-pointers (reg RSP) for the IO privilege level 0 through 2. + pub rsp: [u64; 3], + /// Reserved. + pub _reserved2: u32, + /// Reserved. + pub _reserved3: u32, + pub ist: [u64; 7], + /// Reserved. + pub _reserved4: u32, + /// Reserved. + pub _reserved5: u32, + // Reserved. + pub reserved6: u16, + /// The offset to the IOPB. + pub iomap_base: u16, +} diff --git a/lib.rs b/lib.rs new file mode 100644 index 0000000..23c0a18 --- /dev/null +++ b/lib.rs @@ -0,0 +1,108 @@ +//! # The Redox OS Kernel, version 2 +//! +//! The Redox OS Kernel is a hybrid kernel that supports X86 systems and +//! provides Unix-like syscalls for primarily Rust applications +//! +//! ## Syscalls +//! Syscalls in Redox are often handled by userspace `schemes`. +//! The essential syscalls in Redox are as follows: +//! +//! ### Open +//! `open(path: &str, flags: usize) -> Result` +//! +//! Open a file, providing a path as a `&str` and flags, defined elsewhere. +//! +//! Returns a number, known as a file descriptor, that is passed to other syscalls +//! +//! ### Close +//! `close(file_descriptor: usize) -> Result<()>` +//! +//! Close a file descriptor, providing the file descriptor from `open` +//! +//! Returns an error, `EBADF`, if the file descriptor was not found. +//! +//! This potential error is often ignored by userspace +//! +//! ### Duplicate +//! `dup(file_descriptor: usize) -> Result` +//! +//! Duplicate a file descriptor, providing the file descriptor from `open` +//! +//! Returns a new file descriptor, or an error +//! +//! ### Read +//! `read(file_descriptor: usize, buffer: &mut [u8]) -> Result` +//! +//! Read from a file descriptor, providing the file descriptor from `open` and a mutable buffer +//! +//! Returns the number of bytes actually read, or an error +//! +//! ### Write +//! `write(file_descriptor: usize, buffer: &[u8]) -> Result` +//! +//! Write to a file descriptor, providing the file descriptor from `open` and a const buffer +//! +//! Returns the number of bytes actually written, or an error +//! +//! ### Stat +//! `fstat(file_descriptor: usize, stat: &mut Stat) -> Result<()>` +//! +//! Get information from a file descriptor, providing the file descriptor from `open` +//! and a mutable Stat struct, defined elsewhere. +//! +//! Returns an error if the operation failed +//! +//! ### Path +//! `fpath(file_descriptor: usize, buffer: &mut [u8]) -> Result` +//! +//! Read the path of a file descriptor, providing the file descriptor from `open` and +//! a mutable buffer. +//! +//! Returns the number of bytes actually read, or an error +//! +//! The buffer should be 4096 bytes, to ensure that the entire path will fit. +//! An error will be returned, `ENOBUFS`, if the buffer is not long enough for the name. +//! In this case, it is recommended to add one page, 4096 bytes, to the buffer and retry. + +#![feature(asm)] +#![feature(const_fn)] +#![feature(core_intrinsics)] +#![feature(lang_items)] +#![feature(naked_functions)] +#![no_std] + +#[macro_use] +extern crate bitflags; + +/// Print to console +macro_rules! print { + ($($arg:tt)*) => ({ + use $crate::core::fmt::Write; + let _ = write!($crate::arch::serial::SerialConsole::new(), $($arg)*); + }); +} + +/// Print with new line to console +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +/// Architecture specific items +pub mod arch; + +#[cfg(not(test))] +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[cfg(not(test))] +/// Required to handle panics +#[lang = "panic_fmt"] +extern "C" fn panic_fmt() -> ! {loop{}} + +#[allow(non_snake_case)] +#[no_mangle] +/// Required to handle panics +pub extern "C" fn _Unwind_Resume() -> ! { + loop {} +} diff --git a/scheme/mod.rs b/scheme/mod.rs new file mode 100644 index 0000000..5a44354 --- /dev/null +++ b/scheme/mod.rs @@ -0,0 +1,12 @@ +/// A scheme is a primitive for handling filesystem syscalls in Redox. +/// Schemes accept paths from the kernel for `open`, and file descriptors that they generate +/// are then passed for operations like `close`, `read`, `write`, etc. +/// +/// The kernel validates paths and file descriptors before they are passed to schemes, +/// also stripping the scheme identifier of paths if necessary. +pub trait Scheme { + /// Open the file at `path` with `flags`. + /// + /// Returns a file descriptor or an error + fn open(path: &str, flags: usize) -> Result; +}