Skip to content

Commit

Permalink
wip: add code for lab 2
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed Jan 16, 2024
1 parent 3a926e9 commit 2f928d7
Show file tree
Hide file tree
Showing 13 changed files with 635 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docs/labs/0x02/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ lazy_static! {

对于软件中断,如在 `x86` 架构中的系统调用 `int 0x80`,我们将在 `syscall.rs` 中进行处理。从而统一地对中断进行代码组织。这部分内容将在后续实验中进行实现。

之后按照项目规范,为 `interrupts` 模块添加 `pub fn init()` 函数,将中断系统的初始化工作统一起来:
之后按照项目规范,为 `interrupt` 模块添加 `pub fn init()` 函数,将中断系统的初始化工作统一起来:

```rust
/// init interrupts system
/// init interrupt system
pub fn init() {
// Load the Interrupt Descriptor Table
IDT.load();
Expand Down
17 changes: 17 additions & 0 deletions src/0x02/pkg/kernel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "ysos_kernel"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
boot = { package = "ysos_boot", path = "../boot", default-features = false }
lazy_static = { version = "1.4", features = ["spin_no_std"] }
paste = "1.0"
spin = "0.9"
x86 = "0.52"
x86_64 = "0.14"
log = "0.4"
bitflags = "2.3"
libm = "0.2"
linked_list_allocator = "0.10"
90 changes: 90 additions & 0 deletions src/0x02/pkg/kernel/src/interrupt/apic/ioapic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/// The I/O APIC manages hardware interrupts for an SMP system.
///
/// [Intel Doc](http://www.intel.com/design/chipsets/datashts/29056601.pdf)
/// Default physical address of IO APIC
pub const IOAPIC_ADDR: u64 = 0xFEC00000;

bitflags! {
/// The redirection table starts at REG_TABLE and uses
/// two registers to configure each interrupt.
/// The first (low) register in a pair contains configuration bits.
/// The second (high) register contains a bitmask telling which
/// CPUs can serve that interrupt.
struct RedirectionEntry: u32 {
/// Interrupt disabled
const DISABLED = 0x00010000;
/// Level-triggered (vs edge-)
const LEVEL = 0x00008000;
/// Active low (vs high)
const ACTIVELOW = 0x00002000;
/// Destination is CPU id (vs APIC ID)
const LOGICAL = 0x00000800;
/// None
const NONE = 0x00000000;
}
}


pub struct IoApic {
reg: *mut u32,
data: *mut u32,
}

impl IoApic {
pub unsafe fn new(addr: u64) -> Self {
IoApic {
reg: addr as *mut u32,
data: (addr + 0x10) as *mut u32,
}
}

pub fn disable_all(&mut self) {
// Mark all interrupts edge-triggered, active high, disabled,
// and not routed to any CPUs.
for i in 0..=self.maxintr() {
self.write_irq(i, RedirectionEntry::DISABLED, 0);
}
}

unsafe fn read(&mut self, reg: u8) -> u32 {
self.reg.write_volatile(reg as u32);
self.data.read_volatile()
}

unsafe fn write(&mut self, reg: u8, data: u32) {
self.reg.write_volatile(reg as u32);
self.data.write_volatile(data);
}

fn write_irq(&mut self, irq: u8, flags: RedirectionEntry, dest: u8) {
unsafe {
self.write(0x10 + 2 * irq, (32 + irq) as u32 | flags.bits());
self.write(0x10 + 2 * irq + 1, (dest as u32) << 24);
}
}

pub fn enable(&mut self, irq: u8, cpunum: u8) {
// Mark interrupt edge-triggered, active high,
// enabled, and routed to the given cpunum,
// which happens to be that cpu's APIC ID.
self.write_irq(irq, RedirectionEntry::NONE, cpunum);
trace!("Enable IOApic: IRQ={}, CPU={}", irq, cpunum);
}

pub fn disable(&mut self, irq: u8) {
self.write_irq(irq, RedirectionEntry::DISABLED, 0);
}

pub fn id(&mut self) -> u8 {
unsafe { self.read(0x00).get_bits(24..28) as u8 }
}

pub fn version(&mut self) -> u8 {
unsafe { self.read(0x01).get_bits(0..8) as u8 }
}

pub fn maxintr(&mut self) -> u8 {
unsafe { self.read(0x01).get_bits(16..24) as u8 }
}
}
36 changes: 36 additions & 0 deletions src/0x02/pkg/kernel/src/interrupt/apic/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! APIC (Advanced Programmable Interrupt Controller)
//!
//! For x86 kernel multi-core support.
//!
//! Migrate from:
//! * [Redox](https://github.com/redox-os/kernel/blob/master/src/arch/x86_64/device/local_apic.rs)
//! * [sv6](https://github.com/aclements/sv6/blob/master/kernel/xapic.cc)
//!
//! Reference: [OSDev Wiki](https://wiki.osdev.org/APIC)
pub use ioapic::{IoApic, IOAPIC_ADDR};
pub use xapic::{XApic, LAPIC_ADDR};

mod ioapic;
mod xapic;

pub trait LocalApic {
/// If this type APIC is supported
fn support() -> bool;

/// Initialize the LAPIC for the current CPU
fn cpu_init(&mut self);

/// Return this CPU's LAPIC ID
fn id(&self) -> u32;

fn version(&self) -> u32;

/// Interrupt Command Register
fn icr(&self) -> u64;

fn set_icr(&mut self, value: u64);

/// Acknowledge interrupt on the current CPU
fn eoi(&mut self);
}
98 changes: 98 additions & 0 deletions src/0x02/pkg/kernel/src/interrupt/apic/xapic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use super::LocalApic;
use bit_field::BitField;
use core::fmt::{Debug, Error, Formatter};
use core::ptr::{read_volatile, write_volatile};
use x86::cpuid::CpuId;

/// Default physical address of xAPIC
pub const LAPIC_ADDR: u64 = 0xFEE00000;

pub struct XApic {
addr: u64,
}

impl XApic {
pub unsafe fn new(addr: u64) -> Self {
XApic { addr }
}

unsafe fn read(&self, reg: u32) -> u32 {
read_volatile((self.addr + reg as u64) as *const u32)
}

unsafe fn write(&mut self, reg: u32, value: u32) {
write_volatile((self.addr + reg as u64) as *mut u32, value);
self.read(0x20);
}
}

impl LocalApic for XApic {
/// If this type APIC is supported
fn support() -> bool {
// FIXME: Check CPUID to see if xAPIC is supported.
}

/// Initialize the xAPIC for the current CPU.
fn cpu_init(&mut self) {
unsafe {
// FIXME: Enable local APIC; set spurious interrupt vector.

// FIXME: The timer repeatedly counts down at bus frequency

// FIXME: Disable logical interrupt lines.

// FIXME: Disable performance counter overflow interrupts
// on machines that provide that interrupt entry.

// FIXME: Map error interrupt to IRQ_ERROR.

// FIXME: Clear error status register (requires back-to-back writes).

// FIXME: Ack any outstanding interrupts.

// FIXME: Send an Init Level De-Assert to synchronise arbitration ID's.

// FIXME: Enable interrupts on the APIC (but not on the processor).
}

// NOTE: Try to use bitflags! macro to set the flags.
}

fn id(&self) -> u32 {
// NOTE: Maybe you can handle regs like `0x0300` as a const.
unsafe { self.read(0x0020) >> 24 }
}

fn version(&self) -> u32 {
unsafe { self.read(0x0030) }
}

fn icr(&self) -> u64 {
unsafe { (self.read(0x0310) as u64) << 32 | self.read(0x0300) as u64 }
}

fn set_icr(&mut self, value: u64) {
unsafe {
while self.read(0x0300).get_bit(12) {}
self.write(0x0310, (value >> 32) as u32);
self.write(0x0300, value as u32);
while self.read(0x0300).get_bit(12) {}
}
}

fn eoi(&mut self) {
unsafe {
self.write(0x00B0, 0);
}
}
}

impl Debug for XApic {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
f.debug_struct("Xapic")
.field("id", &self.id())
.field("version", &self.version())
.field("icr", &self.icr())
.finish()
}
}
48 changes: 48 additions & 0 deletions src/0x02/pkg/kernel/src/interrupt/consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This is from https://github.com/rcore-os/rCore/blob/master/kernel/src/arch/x86_64/interrupt/consts.rs
// Reference: https://wiki.osdev.org/Exceptions

#[repr(u8)]
pub enum Interrupts {
DivideError = 0,
Debug = 1,
NonMaskableInterrupt = 2,
Breakpoint = 3,
Overflow = 4,
BoundRangeExceeded = 5,
InvalidOpcode = 6,
DeviceNotAvailable = 7,
DoubleFault = 8,
CoprocessorSegmentOverrun = 9,
InvalidTSS = 10,
SegmentNotPresent = 11,
StackSegmentFault = 12,
GeneralProtectionFault = 13,
PageFault = 14,
FloatingPointException = 16,
AlignmentCheck = 17,
MachineCheck = 18,
SIMDFloatingPointException = 19,
VirtualizationException = 20,
SecurityException = 30,

IrqBase = 0x20,
Syscall = 0x80,
}

/// https://www.computerhope.com/jargon/i/irq.htm
/// https://wiki.osdev.org/IRQ
/// https://github.com/qemu/qemu/blob/aab8cfd4c3614a049b60333a3747aedffbd04150/include/hw/i386/microvm.h#L30-L50
#[repr(u8)]
pub enum Irq {
Timer = 0,
Keyboard = 1,
Serial1 = 3,
Serial0 = 4,
Floppy = 6,
Parallel = 7,
RealTimeClock = 8,
Ide0 = 14,
Ide1 = 15,
Error = 19,
Spurious = 31,
}
41 changes: 41 additions & 0 deletions src/0x02/pkg/kernel/src/interrupt/exceptions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::memory::*;
use x86_64::registers::control::Cr2;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};

pub unsafe fn register_idt(idt: &mut InterruptDescriptorTable) {
idt.divide_error.set_handler_fn(divide_error_handler);
idt.double_fault
.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
idt.page_fault
.set_handler_fn(page_fault_handler)
.set_stack_index(gdt::PAGE_FAULT_IST_INDEX);

// FIXME: handle other exceptions
}

pub extern "x86-interrupt" fn divide_error_handler(stack_frame: InterruptStackFrame) {
panic!("EXCEPTION: DIVIDE ERROR\n\n{:#?}", stack_frame);
}

pub extern "x86-interrupt" fn double_fault_handler(
stack_frame: InterruptStackFrame,
error_code: u64,
) -> ! {
panic!(
"EXCEPTION: DOUBLE FAULT, ERROR_CODE: 0x{:016x}\n\n{:#?}",
error_code, stack_frame
);
}

pub extern "x86-interrupt" fn page_fault_handler(
stack_frame: InterruptStackFrame,
err_code: PageFaultErrorCode,
) {
panic!(
"EXCEPTION: PAGE FAULT, ERROR_CODE: {:?}\n\nTrying to access: {:#x}\n{:#?}",
err_code,
Cr2::read(),
stack_frame
);
}
38 changes: 38 additions & 0 deletions src/0x02/pkg/kernel/src/interrupt/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
mod apic;
mod consts;
// mod clock;
// mod serial;
mod exceptions;

use apic::*;
use x86_64::structures::idt::InterruptDescriptorTable;
use crate::memory::physical_to_virtual;

lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
unsafe {
exceptions::register_idt(&mut idt);
// TODO: clock::register_idt(&mut idt);
// TODO: serial::register_idt(&mut idt);
}
idt
};
}

/// init interrupts system
pub fn init() {
IDT.load();

// FIXME: check and init APIC

// FIXME: enable serial irq with IO APIC (use enable_irq)

info!("Interrupts Initialized.");
}

#[inline(always)]
pub fn enable_irq(irq: u8) {
let mut ioapic = unsafe { IoApic::new(physical_to_virtual(IOAPIC_ADDR)) };
ioapic.enable(irq, 0);
}
Loading

0 comments on commit 2f928d7

Please sign in to comment.