Got interrupts working!
parent
5445ead12b
commit
07715b614b
|
@ -0,0 +1,2 @@
|
|||
#[cfg(target_arch="x86_64")]
|
||||
pub mod x86_64;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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!");
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue