Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into paging
Browse files Browse the repository at this point in the history
0xf4lc0n authored Oct 27, 2023
2 parents 7ad29c4 + 2994a42 commit cc3bd94
Showing 9 changed files with 175 additions and 13 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -26,15 +26,11 @@ apt install qemu-system-x86

### Windows
- Install QEMU from [here](https://qemu.weilnetz.de/w64/)
- Add toolchain
- Add qemu to PATH (example: `C:\Program Files\qemu`)
- Add nightly toolchain
```
rustup component add rust-src --toolchain nightly-x86_64-pc-windows-msvc
```
- Install llvm
```
rustup component add llvm-tools-preview
```
- Add qemu to PATH (example: `C:\Program Files\qemu`)
# Run
This will build the kernel, create an image and launch it with qemu.
3 changes: 2 additions & 1 deletion kernel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -12,4 +12,5 @@ bootloader_api = "0.11.4"
noto-sans-mono-bitmap = "0.2.0"
spin = "0.9.8"
x86_64 = "0.14.10"

pic8259 = "0.10.4"
pc-keyboard = "0.7.0"
137 changes: 136 additions & 1 deletion kernel/src/interrupt.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use crate::println;
use crate::{print, println};
use core::fmt::Debug;
use pc_keyboard::{HandleControl, Keyboard};
use pic8259::ChainedPics;
use spin::once::Once;
use x86_64::registers::control::Cr2;
use spin::Mutex;
use x86_64::structures::idt::PageFaultErrorCode;
use x86_64::{
instructions,
instructions::port::Port,
instructions::tables,
registers::segmentation::{Segment as _, CS, SS},
structures::{
@@ -13,20 +19,58 @@ use x86_64::{
VirtAddr,
};

type SupportedKeyboard = Keyboard<pc_keyboard::layouts::Us104Key, pc_keyboard::ScancodeSet1>;

static IDT: Once<InterruptDescriptorTable> = Once::new();
static TSS: Once<TaskStateSegment> = Once::new();
static GDT: Once<GlobalDescriptorTable> = Once::new();
static SEGMENT_SELECTORS: Once<SegmentSelectors> = Once::new();
static PICS: Once<Mutex<ChainedPics>> = Once::new();
static KEYBOARD: Once<Mutex<SupportedKeyboard>> = Once::new();

const DOUBLE_FAULT_IST_INDEX: u16 = 0;

// hardware interrupts PICs (slots 32-47)
// Safety - ensure that the PICs does not overlap
const PIC_1_OFFSET: u8 = 32;
const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;

const PS2_CONTROLLER_PORT: u16 = 0x60;

/// Initialize interrupt handlers.
///
/// # Panics
/// This function will panic if it is called more than once.
pub fn init() {
init_gdt();
init_idt();
init_pic();
}

/// Enable interrupts if they are not enabled.
pub fn enable_interrupts() {
if !instructions::interrupts::are_enabled() {
instructions::interrupts::enable();
}
}

#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = PIC_1_OFFSET,
Keyboard,
}

impl From<InterruptIndex> for u8 {
fn from(value: InterruptIndex) -> Self {
value as u8
}
}

impl From<InterruptIndex> for usize {
fn from(value: InterruptIndex) -> Self {
value as usize
}
}

#[derive(Debug)]
@@ -49,6 +93,8 @@ struct SegmentSelectors {
fn init_idt() {
let idt = IDT.call_once(|| {
let mut idt = InterruptDescriptorTable::new();

// # exceptions
idt.alignment_check.set_handler_fn(alignment_check_handler);
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.bound_range_exceeded
@@ -71,6 +117,10 @@ fn init_idt() {
.set_handler_fn(double_fault_handler)
.set_stack_index(DOUBLE_FAULT_IST_INDEX);
}

idt[InterruptIndex::Timer.into()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.into()].set_handler_fn(keyboard_interrupt_handler);

idt
});
idt.load();
@@ -129,6 +179,45 @@ fn init_gdt() {
}
}

/// Initialize Programmable Interrupt Controllers (PICs).
///
/// This functions setups the standard PIC 1 and PIC 2 controllers to properly handle hardware
/// interrupts.
///
/// # Panics
/// This function will panic if it is called more than once.
fn init_pic() {
let pics = PICS.call_once(|| {
// # Safety
// we ensure that the PICs does not overlap and the offsets are correct
let chained_pics = unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) };

Mutex::new(chained_pics)
});

// initialize keyboard before enabling interrupts
init_keyboard();

// # Safety
// we ensure that the PICs are properly configured
unsafe {
pics.lock().initialize();
}
}

fn init_keyboard() {
KEYBOARD.call_once(|| {
let keyboard = Keyboard::new(
pc_keyboard::ScancodeSet1::new(),
pc_keyboard::layouts::Us104Key,
HandleControl::Ignore,
);

Mutex::new(keyboard)
});
}

// Exception handlers
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
// FIXME handle breakpoint
println!("Exception: breakpoint\n{:#?}", frame)
@@ -196,3 +285,49 @@ extern "x86-interrupt" fn double_fault_handler(frame: InterruptStackFrame, _erro
// FIXME handle double fault
panic!("Exception: double fault\n{:#?}", frame);
}

extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
// # Safety
// we use the same interrupt number as the handler is registered for
unsafe {
PICS.get()
.unwrap()
.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.into());
}
}

extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
// # Safety
// we read from the keyboard port only on keyboard interrupt
let scancode: u8 = unsafe { Port::new(PS2_CONTROLLER_PORT).read() };

let mut keyboard = KEYBOARD.get().unwrap().lock();

match keyboard.add_byte(scancode) {
Ok(Some(key_event)) => {
if let Some(key) = keyboard.process_keyevent(key_event) {
use pc_keyboard::DecodedKey::Unicode;
match key {
Unicode('\x1b') => print!("ESC"),
Unicode('\x08') => print!("BS"),
Unicode('\x7f') => print!("DEL"),
Unicode('\t') => print!("TAB"), // FIXME: tab not supported in logger?
Unicode(character) => print!("{}", character),
pc_keyboard::DecodedKey::RawKey(key) => print!("RAW[{:?}]", key),
}
}
}
Ok(None) => {}
Err(e) => println!("Keyboard error: {:?}", e),
}

// # Safety
// we use the same interrupt number as the handler is registered for
unsafe {
PICS.get()
.unwrap()
.lock()
.notify_end_of_interrupt(InterruptIndex::Keyboard.into());
}
}
8 changes: 8 additions & 0 deletions kernel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -36,4 +36,12 @@ pub fn init(boot_info: &'static mut BootInfo) {
};

memory::init_global(physical_memory_offset, &boot_info.memory_regions);
interrupt::enable_interrupts();
}

pub fn halt_loop() -> ! {
loop {
// halts until next interrupt
x86_64::instructions::hlt();
}
}
5 changes: 4 additions & 1 deletion kernel/src/logger.rs
Original file line number Diff line number Diff line change
@@ -21,7 +21,10 @@ macro_rules! println {
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
LOGGER.get().unwrap().lock().write_fmt(args).unwrap();
// FIXME (temporary solution): disable interrupts while printing
x86_64::instructions::interrupts::without_interrupts(|| {
LOGGER.get().unwrap().lock().write_fmt(args).unwrap();
})
}

static LOGGER: Once<Mutex<Logger<'static>>> = Once::new();
6 changes: 2 additions & 4 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
@@ -13,12 +13,10 @@ fn main(boot_info: &'static mut BootInfo) -> ! {

println!("it works");

#[allow(clippy::empty_loop)]
loop {}
kernel::halt_loop();
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("{info}");
loop {}
kernel::halt_loop();
}
1 change: 1 addition & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[toolchain]
channel = "nightly"
components = ["llvm-tools-preview"]
targets = ["x86_64-unknown-none"]
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,9 @@ fn main() -> eyre::Result<()> {
.arg(format!("format=raw,file={bios_path}"))
.arg("-boot")
.arg(format!("menu=on,splash={bootsplash_path},splash-time=1000"))
.arg("-no-reboot")
.arg("-d")
.arg("cpu_reset")
.spawn()?
.wait()?;

0 comments on commit cc3bd94

Please sign in to comment.