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 terminal;
|
||||||
pub mod uefi;
|
pub mod uefi;
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use crate::graphics::tty::serial::SerialTty;
|
use crate::graphics::tty::serial::SerialTty;
|
||||||
|
|
||||||
pub trait Tty {
|
pub trait Tty {
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use alloc::string::String;
|
|
||||||
use crate::graphics::tty::Tty;
|
use crate::graphics::tty::Tty;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SerialTty {
|
pub struct SerialTty {
|
||||||
port: u16,
|
port: u16,
|
||||||
buffer: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerialTty {
|
impl SerialTty {
|
||||||
pub unsafe fn new(port: u16) -> SerialTty {
|
pub unsafe fn new(port: u16) -> SerialTty {
|
||||||
SerialTty {
|
SerialTty {
|
||||||
port: port,
|
port: port,
|
||||||
buffer: String::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,15 +17,25 @@ impl SerialTty {
|
||||||
asm!("out dx, al", in("dx") self.port, in("al") cmd);
|
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 {
|
impl Tty for SerialTty {
|
||||||
fn putc(&mut self, c: char) {
|
fn putc(&mut self, c: char) {
|
||||||
self.buffer.push(c);
|
self.outc(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn puts(&mut self, s: &str) {
|
fn puts(&mut self, s: &str) {
|
||||||
self.buffer.push_str(s);
|
for c in s.chars() {
|
||||||
|
self.putc(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
|
@ -37,9 +44,6 @@ impl Tty for SerialTty {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) {
|
fn flush(&mut self) {
|
||||||
for b in self.buffer.bytes() {
|
// This TTY doesn't support buffering.
|
||||||
self.outb(b);
|
|
||||||
}
|
|
||||||
self.buffer.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl Tty for UefiTty<'_> {
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
// VT100 escape code to reset the terminal: `ESC C`.
|
// VT100 escape code to reset the terminal: `ESC C`.
|
||||||
self.output.clear();
|
self.output.clear().unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) {
|
fn flush(&mut self) {
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -3,14 +3,16 @@
|
||||||
#![feature(abi_efiapi)]
|
#![feature(abi_efiapi)]
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
#![feature(abi_x86_interrupt)]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod allocator;
|
mod allocator;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod graphics;
|
mod graphics;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
mod arch;
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use crate::allocator::{Allocator, ALLOCATOR};
|
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.");
|
st.stdout().reset(false).expect_success("Failed to reset UEFI stdout.");
|
||||||
|
|
||||||
println!("Booting...");
|
println!("Booting...");
|
||||||
use core::fmt::Write;
|
|
||||||
|
|
||||||
for entry in st.config_table() {
|
for entry in st.config_table() {
|
||||||
use uefi::table::cfg::*;
|
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) -> ! {
|
fn main(_st: SystemTable<uefi::table::Runtime>, _mmap: uefi::table::boot::MemoryMapIter) -> ! {
|
||||||
|
crate::arch::x86_64::breakpoint();
|
||||||
|
|
||||||
halt()
|
halt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,9 +55,9 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
||||||
// Tasks that require the UEFI boot services.
|
// Tasks that require the UEFI boot services.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
ALLOCATOR = Allocator::Uefi(st_boot.unsafe_clone());
|
||||||
STDOUT = Some(SerialTty::new(0x3F8));
|
STDOUT = Some(SerialTty::new(0x3F8));
|
||||||
STDERR = Some(SerialTty::new(0x3F8));
|
STDERR = Some(SerialTty::new(0x3F8));
|
||||||
ALLOCATOR = Allocator::Uefi(st_boot.unsafe_clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(&st_boot, handle);
|
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.")
|
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.
|
// Tasks that do not require the UEFI boot services.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
Loading…
Reference in New Issue