Got interrupts working!

master
James T. Martin 2020-07-17 16:51:24 -07:00
parent 5445ead12b
commit 07715b614b
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
8 changed files with 90 additions and 14 deletions

2
src/arch/mod.rs Normal file
View File

@ -0,0 +1,2 @@
#[cfg(target_arch="x86_64")]
pub mod x86_64;

23
src/arch/x86_64/gdt.rs Normal file
View File

@ -0,0 +1,23 @@
use x86_64::instructions::segmentation::{load_ss, set_cs};
use x86_64::structures::gdt::{Descriptor, DescriptorFlags, GlobalDescriptorTable};
static mut GDT: GlobalDescriptorTable = GlobalDescriptorTable::new();
fn kernel_data_segment() -> Descriptor {
use self::DescriptorFlags as Flags;
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::WRITABLE;
Descriptor::UserSegment(flags.bits())
}
pub fn load() {
unsafe {
let cs = GDT.add_entry(Descriptor::kernel_code_segment());
GDT.add_entry(Descriptor::user_code_segment());
GDT.add_entry(Descriptor::user_data_segment());
let ss = GDT.add_entry(kernel_data_segment());
GDT.load();
set_cs(cs);
load_ss(ss);
}
}

18
src/arch/x86_64/idt.rs Normal file
View File

@ -0,0 +1,18 @@
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();
pub fn load() {
unsafe {
IDT.load();
IDT.breakpoint.set_handler_fn(breakpoint);
}
}
extern "x86-interrupt" fn breakpoint(_: &mut InterruptStackFrame) {
use crate::graphics::tty::Tty;
use crate::graphics::tty::serial::SerialTty;
let mut stdout = unsafe { SerialTty::new(0x3F8) };
stdout.puts("Breakpoint reached!");
}

18
src/arch/x86_64/mod.rs Normal file
View File

@ -0,0 +1,18 @@
pub mod gdt;
pub mod idt;
/// This macro exists because the x86_64 library uses `llvm_asm!`, which I have disabled.
/// When the library ever uses plain `asm!` or a function, I will use its version instead.
#[cfg(target_arch="x86_64")]
#[macro_export]
macro_rules! software_interrupt {
($x:expr) => {
asm!("int {}", const $x);
}
}
pub fn breakpoint() {
unsafe {
asm!("int3");
}
}

View File

@ -2,7 +2,6 @@ pub mod serial;
pub mod terminal;
pub mod uefi;
use alloc::boxed::Box;
use crate::graphics::tty::serial::SerialTty;
pub trait Tty {

View File

@ -1,17 +1,14 @@
use alloc::string::String;
use crate::graphics::tty::Tty;
#[derive(Clone)]
pub struct SerialTty {
port: u16,
buffer: String,
}
impl SerialTty {
pub unsafe fn new(port: u16) -> SerialTty {
SerialTty {
port: port,
buffer: String::new(),
}
}
@ -20,15 +17,25 @@ impl SerialTty {
asm!("out dx, al", in("dx") self.port, in("al") cmd);
}
}
fn outc(&self, c: char) {
let len = c.len_utf8();
let bytes = (c as u32).to_le_bytes();
for i in 0..len {
self.outb(bytes[i]);
}
}
}
impl Tty for SerialTty {
fn putc(&mut self, c: char) {
self.buffer.push(c);
self.outc(c);
}
fn puts(&mut self, s: &str) {
self.buffer.push_str(s);
for c in s.chars() {
self.putc(c);
}
}
fn clear(&mut self) {
@ -37,9 +44,6 @@ impl Tty for SerialTty {
}
fn flush(&mut self) {
for b in self.buffer.bytes() {
self.outb(b);
}
self.buffer.clear();
// This TTY doesn't support buffering.
}
}

View File

@ -30,7 +30,7 @@ impl Tty for UefiTty<'_> {
fn clear(&mut self) {
// VT100 escape code to reset the terminal: `ESC C`.
self.output.clear();
self.output.clear().unwrap().unwrap();
}
fn flush(&mut self) {

View File

@ -3,14 +3,16 @@
#![feature(abi_efiapi)]
#![feature(alloc_error_handler)]
#![feature(asm)]
#![feature(naked_functions)]
#![feature(abi_x86_interrupt)]
extern crate alloc;
mod allocator;
#[macro_use]
mod graphics;
mod misc;
mod arch;
use alloc::boxed::Box;
use core::mem;
use core::slice;
use crate::allocator::{Allocator, ALLOCATOR};
@ -24,7 +26,6 @@ fn setup(st: &SystemTable<Boot>, _handle: Handle) {
st.stdout().reset(false).expect_success("Failed to reset UEFI stdout.");
println!("Booting...");
use core::fmt::Write;
for entry in st.config_table() {
use uefi::table::cfg::*;
@ -44,6 +45,8 @@ fn setup(st: &SystemTable<Boot>, _handle: Handle) {
}
fn main(_st: SystemTable<uefi::table::Runtime>, _mmap: uefi::table::boot::MemoryMapIter) -> ! {
crate::arch::x86_64::breakpoint();
halt()
}
@ -52,9 +55,9 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
// Tasks that require the UEFI boot services.
unsafe {
ALLOCATOR = Allocator::Uefi(st_boot.unsafe_clone());
STDOUT = Some(SerialTty::new(0x3F8));
STDERR = Some(SerialTty::new(0x3F8));
ALLOCATOR = Allocator::Uefi(st_boot.unsafe_clone());
}
setup(&st_boot, handle);
@ -75,6 +78,15 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
st_boot.exit_boot_services(handle, mmap_buf_slice).expect_success("Failed to exit the UEFI boot services.")
};
// Replace the GDT and IDT with my own so I can start handling interrupts.
use x86_64::instructions::interrupts;
use crate::arch::x86_64::{gdt, idt};
interrupts::disable();
gdt::load();
idt::load();
interrupts::enable();
// Tasks that do not require the UEFI boot services.
unsafe {