Remove interrupt setup from asm bootloader, add io module, memcpy functions, and serial driver

This commit is contained in:
Jeremy Soller 2016-08-14 09:31:35 -06:00
parent 799b77d11a
commit 42c9ba12dc
17 changed files with 422 additions and 264 deletions

View file

@ -5,6 +5,9 @@ version = "0.1.0"
[lib] [lib]
crate-type = ["staticlib"] crate-type = ["staticlib"]
[dependencies]
bitflags = "*"
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"

View file

@ -10,16 +10,16 @@ bochs: build/harddrive.bin
bochs -f bochs.$(ARCH) bochs -f bochs.$(ARCH)
qemu: build/harddrive.bin qemu: build/harddrive.bin
qemu-system-$(ARCH) -drive file=$<,format=raw,index=0,media=disk qemu-system-$(ARCH) -serial mon:stdio -drive file=$<,format=raw,index=0,media=disk
FORCE: FORCE:
build/libkernel.a: FORCE build/libkernel.a: FORCE
rustc --crate-type staticlib -C lto -O src/lib.rs -o $@ cargo rustc -- -C lto -o $@
#--target $(ARCH)-unknown-none.json #--target $(ARCH)-unknown-none.json
build/kernel.bin: build/libkernel.a build/kernel.bin: build/libkernel.a
ld -m elf_$(ARCH) -o $@ -T bootloader/x86/kernel.ld -z max-page-size=0x1000 $< ld -m elf_$(ARCH) --gc-sections -z max-page-size=0x1000 -T bootloader/x86/kernel.ld -o $@ $<
build/kernel.list: build/kernel.bin build/kernel.list: build/kernel.bin
objdump -C -M intel -D $< > $@ objdump -C -M intel -D $< > $@

View file

@ -1,105 +0,0 @@
struc IDTEntry
.offsetl resw 1
.selector resw 1
.zero resb 1
.attribute resb 1
.offseth resw 1
endstruc
SECTION .text
USE32
interrupts:
.first:
mov [.entry], byte 0
jmp dword .handle
.second:
%assign i 1
%rep 255
mov [.entry], byte i
jmp dword .handle
%assign i i+1
%endrep
.handle:
push ebp
push esi
push edi
push edx
push ecx
push ebx
push eax
push esp
push dword [.entry]
mov eax, gdt.kernel_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
call dword [.handler]
mov eax, gdt.user_data | 3
mov ds, eax
mov es, eax
mov fs, eax
mov eax, gdt.user_tls | 3
mov gs, eax
add esp, 8 ; Skip interrupt code and reg pointer
pop eax
pop ebx
pop ecx
pop edx
pop edi
pop esi
pop ebp
iretd
.handler: dd 0
.entry: dd 0
idtr:
dw (idt.end - idt) + 1
dd idt
idt:
%assign i 0
;Below system call
%rep 128
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.zero, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt32
at IDTEntry.offseth, dw 0
iend
%assign i i+1
%endrep
;System call
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.zero, db 0
at IDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.interrupt32
at IDTEntry.offseth, dw 0
iend
%assign i i+1
;Above system call
%rep 127
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.zero, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt32
at IDTEntry.offseth, dw 0
iend
%assign i i+1
%endrep
.end:

View file

@ -1,130 +0,0 @@
struc IDTEntry
.offsetl resw 1
.selector resw 1
.ist resb 1
.attribute resb 1
.offsetm resw 1
.offseth resd 1
.reserved resd 1
endstruc
SECTION .text
USE64
interrupts:
.first:
mov [.entry], byte 0
jmp qword .handle
.second:
%assign i 1
%rep 255
mov [.entry], byte i
jmp qword .handle
%assign i i+1
%endrep
.handle:
push rbp
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push rsi
push rdi
push rdx
push rcx
push rbx
push rax
mov rsi, rsp
push rsi
mov rdi, qword [.entry]
push rdi
mov rax, gdt.kernel_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
call qword [.handler]
mov rax, gdt.user_data | 3
mov ds, rax
mov es, rax
mov gs, rax
mov rax, gdt.user_tls | 3
mov fs, rax
add rsp, 16 ; Skip interrupt code and reg pointer
pop rax
pop rbx
pop rcx
pop rdx
pop rdi
pop rsi
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
pop rbp
iretq
.handler: dq 0
.entry: dq 0
idtr:
dw (idt.end - idt) + 1
dq idt
idt:
%assign i 0
;Below syscall
%rep 128
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.ist, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt64
at IDTEntry.offsetm, dw 0
at IDTEntry.offseth, dd 0
at IDTEntry.reserved, dd 0
iend
%assign i i+1
%endrep
;Syscall
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.ist, db 0
at IDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.interrupt64
at IDTEntry.offsetm, dw 0
at IDTEntry.offseth, dd 0
at IDTEntry.reserved, dd 0
iend
%assign i i+1
;Above syscall
%rep 127
istruc IDTEntry
at IDTEntry.offsetl, dw interrupts+(interrupts.second-interrupts.first)*i
at IDTEntry.selector, dw gdt.kernel_code
at IDTEntry.ist, db 0
at IDTEntry.attribute, db attrib.present | attrib.interrupt64
at IDTEntry.offsetm, dw 0
at IDTEntry.offseth, dd 0
at IDTEntry.reserved, dd 0
iend
%assign i i+1
%endrep
.end:

View file

@ -146,5 +146,3 @@ tss:
at TSS.iomap_base, dw 0xFFFF at TSS.iomap_base, dw 0xFFFF
iend iend
.end: .end:
%include "interrupts-i386.asm"

View file

@ -73,11 +73,8 @@ long_mode:
xor rax, rax xor rax, rax
mov eax, [kernel_base + 0x18] mov eax, [kernel_base + 0x18]
mov rbx, gdtr mov rbx, gdtr
xchg bx, bx
jmp rax jmp rax
.lp:
sti
hlt
jmp .lp
gdtr: gdtr:
dw gdt.end + 1 ; size dw gdt.end + 1 ; size
@ -179,5 +176,3 @@ long_mode:
at TSS.iomap_base, dw 0xFFFF at TSS.iomap_base, dw 0xFFFF
iend iend
.end: .end:
%include "interrupts-x86_64.asm"

View file

@ -1,4 +1,7 @@
//! Architecture specific items //! Architecture specific items
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub mod x86_64; pub mod x86_64;

View file

@ -7,10 +7,23 @@ pub static mut IDTR: IdtDescriptor = IdtDescriptor {
pub static mut IDT: [IdtEntry; 256] = [IdtEntry::new(); 256]; 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)] #[repr(packed)]
pub struct IdtDescriptor { pub struct IdtDescriptor {
pub size: u16, size: u16,
pub offset: u64 offset: u64
} }
impl IdtDescriptor { impl IdtDescriptor {
@ -18,18 +31,22 @@ impl IdtDescriptor {
self.size = (slice.len() * mem::size_of::<IdtEntry>() - 1) as u16; self.size = (slice.len() * mem::size_of::<IdtEntry>() - 1) as u16;
self.offset = slice.as_ptr() as u64; 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)] #[derive(Copy, Clone, Debug)]
#[repr(packed)] #[repr(packed)]
pub struct IdtEntry { pub struct IdtEntry {
pub offsetl: u16, offsetl: u16,
pub selector: u16, selector: u16,
pub zero: u8, zero: u8,
pub attribute: u8, attribute: u8,
pub offsetm: u16, offsetm: u16,
pub offseth: u32, offseth: u32,
pub zero2: u32 zero2: u32
} }
impl IdtEntry { impl IdtEntry {
@ -45,7 +62,12 @@ impl IdtEntry {
} }
} }
pub fn set_offset(&mut self, base: usize) { 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.offsetl = base as u16;
self.offsetm = (base >> 16) as u16; self.offsetm = (base >> 16) as u16;
self.offseth = (base >> 32) as u32; self.offseth = (base >> 32) as u32;

61
src/arch/x86_64/io/io.rs Normal file
View file

@ -0,0 +1,61 @@
use core::cmp::PartialEq;
use core::ops::{BitAnd, BitOr, Not};
pub trait Io {
type Value: Copy + PartialEq + BitAnd<Output = Self::Value> + BitOr<Output = Self::Value> + Not<Output = Self::Value>;
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<I: Io> {
inner: I
}
impl<I: Io> ReadOnly<I> {
pub fn new(inner: I) -> ReadOnly<I> {
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<I: Io> {
inner: I
}
impl<I: Io> WriteOnly<I> {
pub fn new(inner: I) -> WriteOnly<I> {
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)
}
}

View file

@ -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<T> {
value: T,
}
impl<T> Mmio<T> {
/// Create a new Mmio without initializing
pub fn new() -> Self {
Mmio {
value: unsafe { uninitialized() }
}
}
}
impl<T> Io for Mmio<T> where T: Copy + PartialEq + BitAnd<Output = T> + BitOr<Output = T> + Not<Output = T> {
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) };
}
}

View file

@ -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;

83
src/arch/x86_64/io/pio.rs Normal file
View file

@ -0,0 +1,83 @@
use core::marker::PhantomData;
use super::io::Io;
/// Generic PIO
#[derive(Copy, Clone)]
pub struct Pio<T> {
port: u16,
value: PhantomData<T>,
}
impl<T> Pio<T> {
/// Create a PIO from a given port
pub fn new(port: u16) -> Self {
Pio::<T> {
port: port,
value: PhantomData,
}
}
}
/// Read/Write for byte PIO
impl Io for Pio<u8> {
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<u16> {
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<u32> {
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");
}
}
}

View file

@ -3,27 +3,69 @@
/// It must create the IDT with the correct entries, those entries are /// It must create the IDT with the correct entries, those entries are
/// defined in other files inside of the `arch` module /// defined in other files inside of the `arch` module
use super::idt::{IDTR, IDT}; 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;
#[naked]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn kmain() { pub unsafe extern "C" fn kmain() {
asm!("xchg bx, bx" : : : : "intel", "volatile"); 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() { for entry in IDT.iter_mut() {
entry.attribute = 1 << 7 | 0xE; entry.set_flags(IDT_PRESENT | IDT_RING_0 | IDT_INTERRUPT);
entry.selector = 8; entry.set_offset(8, blank as usize);
entry.set_offset(blank as usize);
entry.zero = 0;
entry.zero2 = 0;
} }
IDTR.set_slice(&IDT); IDTR.set_slice(&IDT);
asm!("lidt [rax]" : : "{rax}"(&IDTR as *const _ as usize) : : "intel", "volatile"); IDTR.load();
asm!("xchg bx, bx" : : : : "intel", "volatile"); asm!("xchg bx, bx" : : : : "intel", "volatile");
asm!("int 0xFF" : : : : "intel", "volatile"); asm!("int 0xFF" : : : : "intel", "volatile");
asm!("xchg bx, bx" : : : : "intel", "volatile");
print!("TEST\n");
loop{ loop{
asm!("hlt" : : : : "intel", "volatile"); asm!("hlt" : : : : "intel", "volatile");
} }

70
src/arch/x86_64/mem.rs Normal file
View file

@ -0,0 +1,70 @@
/// Memcpy
///
/// 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
}
/// Memmove
///
/// 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
}
/// Memset
///
/// 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
}
/// Memcmp
///
/// 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
}

View file

@ -4,8 +4,20 @@ pub mod gdt;
/// Interrupt descriptor table /// Interrupt descriptor table
pub mod idt; pub mod idt;
/// IO Handling
pub mod io;
/// IRQ Handling /// IRQ Handling
pub mod irq; pub mod irq;
/// Initialization and main function /// Initialization and main function
pub mod main; pub mod main;
/// Memcpy, memmove, etc.
pub mod mem;
/// Serial driver and print! support
pub mod serial;
/// Task state segment
pub mod tss;

39
src/arch/x86_64/serial.rs Normal file
View file

@ -0,0 +1,39 @@
use core::fmt;
use super::io::{Io, Pio};
pub struct SerialConsole {
status: Pio<u8>,
data: Pio<u8>
}
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(())
}
}

View file

@ -66,10 +66,28 @@
#![feature(asm)] #![feature(asm)]
#![feature(const_fn)] #![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(lang_items)] #![feature(lang_items)]
#![feature(naked_functions)] #![feature(naked_functions)]
#![no_std] #![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 /// Architecture specific items
pub mod arch; pub mod arch;
@ -81,3 +99,10 @@ extern "C" fn eh_personality() {}
/// Required to handle panics /// Required to handle panics
#[lang = "panic_fmt"] #[lang = "panic_fmt"]
extern "C" fn panic_fmt() -> ! {loop{}} extern "C" fn panic_fmt() -> ! {loop{}}
#[allow(non_snake_case)]
#[no_mangle]
/// Required to handle panics
pub extern "C" fn _Unwind_Resume() -> ! {
loop {}
}