Skip to content

Commit b8aa920

Browse files
committed
Step 0-2: handle traps, enable APIC timer
1 parent ab8c9c3 commit b8aa920

15 files changed

Lines changed: 466 additions & 10 deletions

File tree

hypervisor/Cargo.lock

Lines changed: 44 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hypervisor/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ log = "0.4"
1111
spin = "0.9"
1212
cfg-if = "1.0"
1313
bitflags = "1.3"
14+
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
1415

1516
[target.'cfg(target_arch = "x86_64")'.dependencies]
1617
x86 = "0.52"
1718
x86_64 = "0.14"
19+
x2apic = "0.4"
20+
raw-cpuid = "10.6"

hypervisor/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ GDB := gdb-multiarch
2525
qemu := qemu-system-$(ARCH)
2626
qemu_args := -nographic -m 128M
2727

28-
qemu_args += -cpu host -accel kvm
28+
qemu_args += -cpu host,+x2apic -accel kvm
2929

3030
ifeq ($(ARCH), x86_64)
3131
qemu_args += \

hypervisor/src/arch/x86_64/gdt.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use x86_64::instructions::tables::{lgdt, load_tss};
2+
use x86_64::registers::segmentation::{Segment, SegmentSelector, CS};
3+
use x86_64::structures::gdt::{Descriptor, DescriptorFlags};
4+
use x86_64::structures::{tss::TaskStateSegment, DescriptorTablePointer};
5+
use x86_64::{addr::VirtAddr, PrivilegeLevel};
6+
7+
lazy_static::lazy_static! {
8+
static ref TSS: TaskStateSegment = TaskStateSegment::new();
9+
static ref GDT: GdtStruct = GdtStruct::new(&TSS);
10+
}
11+
12+
struct GdtStruct {
13+
table: [u64; 16],
14+
}
15+
16+
impl GdtStruct {
17+
pub const KCODE_SELECTOR: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
18+
pub const _KDATA_SELECTOR: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
19+
pub const TSS_SELECTOR: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring0);
20+
21+
pub fn new(tss: &'static TaskStateSegment) -> Self {
22+
let mut table = [0; 16];
23+
table[1] = DescriptorFlags::KERNEL_CODE64.bits(); // 0x00af9b000000ffff
24+
table[2] = DescriptorFlags::KERNEL_DATA.bits(); // 0x00cf93000000ffff
25+
if let Descriptor::SystemSegment(low, high) = Descriptor::tss_segment(tss) {
26+
table[3] = low;
27+
table[4] = high;
28+
}
29+
Self { table }
30+
}
31+
32+
fn pointer(&self) -> DescriptorTablePointer {
33+
DescriptorTablePointer {
34+
base: VirtAddr::new(self.table.as_ptr() as u64),
35+
limit: (core::mem::size_of_val(&self.table) - 1) as u16,
36+
}
37+
}
38+
39+
pub fn load(&'static self) {
40+
unsafe {
41+
lgdt(&self.pointer());
42+
CS::set_reg(GdtStruct::KCODE_SELECTOR);
43+
}
44+
}
45+
46+
pub fn load_tss(&'static self, selector: SegmentSelector) {
47+
unsafe { load_tss(selector) };
48+
}
49+
}
50+
51+
pub fn init() {
52+
println!("Initializing GDT...");
53+
lazy_static::initialize(&GDT);
54+
GDT.load();
55+
GDT.load_tss(GdtStruct::TSS_SELECTOR);
56+
}

hypervisor/src/arch/x86_64/idt.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use x86_64::structures::idt::{Entry, HandlerFunc, InterruptDescriptorTable};
2+
3+
const NUM_INT: usize = 256;
4+
5+
lazy_static::lazy_static! {
6+
static ref IDT: IdtStruct = IdtStruct::new();
7+
}
8+
9+
struct IdtStruct {
10+
table: InterruptDescriptorTable,
11+
}
12+
13+
impl IdtStruct {
14+
fn new() -> Self {
15+
extern "C" {
16+
#[link_name = "trap_handler_table"]
17+
static ENTRIES: [extern "C" fn(); NUM_INT];
18+
}
19+
let mut idt = Self {
20+
table: InterruptDescriptorTable::new(),
21+
};
22+
23+
let entries = unsafe {
24+
core::slice::from_raw_parts_mut(
25+
&mut idt.table as *mut _ as *mut Entry<HandlerFunc>,
26+
NUM_INT,
27+
)
28+
};
29+
for i in 0..NUM_INT {
30+
entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) });
31+
}
32+
idt
33+
}
34+
35+
fn load(&'static self) {
36+
self.table.load();
37+
}
38+
}
39+
40+
pub fn init() {
41+
println!("Initializing IDT...");
42+
lazy_static::initialize(&IDT);
43+
IDT.load();
44+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![allow(dead_code)]
2+
3+
use core::arch::asm;
4+
5+
use x86_64::registers::{rflags, rflags::RFlags};
6+
7+
#[inline]
8+
pub fn enable_irqs() {
9+
unsafe { asm!("sti") };
10+
}
11+
12+
#[inline]
13+
pub fn disable_irqs() {
14+
unsafe { asm!("cli") };
15+
}
16+
17+
#[inline]
18+
pub fn irqs_disabled() -> bool {
19+
!rflags::read().contains(RFlags::INTERRUPT_FLAG)
20+
}
21+
22+
#[inline]
23+
pub fn wait_for_ints() {
24+
if !irqs_disabled() {
25+
x86_64::instructions::hlt();
26+
}
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use x2apic::lapic::{LocalApic, LocalApicBuilder};
2+
use x86_64::instructions::port::Port;
3+
4+
use self::vectors::*;
5+
6+
pub mod vectors {
7+
pub const APIC_TIMER_VECTOR: u8 = 0xf0;
8+
pub const APIC_SPURIOUS_VECTOR: u8 = 0xf1;
9+
pub const APIC_ERROR_VECTOR: u8 = 0xf2;
10+
}
11+
12+
static mut LOCAL_APIC: Option<LocalApic> = None;
13+
14+
pub fn local_apic<'a>() -> &'a mut LocalApic {
15+
// It's safe as LAPIC is per-cpu.
16+
unsafe { LOCAL_APIC.as_mut().unwrap() }
17+
}
18+
19+
pub fn init() {
20+
println!("Initializing Local APIC...");
21+
22+
unsafe {
23+
// Disable 8259A interrupt controllers
24+
Port::<u8>::new(0x20).write(0xff);
25+
Port::<u8>::new(0xA0).write(0xff);
26+
}
27+
28+
let mut lapic = LocalApicBuilder::new()
29+
.timer_vector(APIC_TIMER_VECTOR as _)
30+
.error_vector(APIC_ERROR_VECTOR as _)
31+
.spurious_vector(APIC_SPURIOUS_VECTOR as _)
32+
.build()
33+
.unwrap();
34+
unsafe {
35+
lapic.enable();
36+
LOCAL_APIC = Some(lapic);
37+
}
38+
}

hypervisor/src/arch/x86_64/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
mod boot;
2+
mod gdt;
3+
mod idt;
4+
mod lapic;
5+
mod trap;
26

7+
pub mod instructions;
8+
pub mod timer;
39
pub mod uart16550;
410

11+
pub use trap::handle_irq;
512
pub use uart16550 as uart;
613

714
pub fn init_early() {
815
uart::init();
916
}
17+
18+
pub fn init() {
19+
gdt::init();
20+
idt::init();
21+
lapic::init();
22+
timer::init();
23+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use raw_cpuid::CpuId;
2+
use x2apic::lapic::{TimerDivide, TimerMode};
3+
4+
use crate::config::TICKS_PER_SEC;
5+
6+
use super::lapic::local_apic;
7+
8+
const LAPIC_TICKS_PER_SEC: u64 = 1_000_000_000; // TODO: need to calibrate
9+
10+
static mut CPU_FREQ_MHZ: u64 = 4_000;
11+
12+
pub fn current_ticks() -> u64 {
13+
unsafe { core::arch::x86_64::_rdtsc() }
14+
}
15+
16+
pub fn ticks_to_nanos(ticks: u64) -> u64 {
17+
ticks * 1_000 / unsafe { CPU_FREQ_MHZ }
18+
}
19+
20+
pub fn init() {
21+
if let Some(freq) = CpuId::new()
22+
.get_processor_frequency_info()
23+
.map(|info| info.processor_base_frequency())
24+
{
25+
if freq > 0 {
26+
println!("Got TSC frequency by CPUID: {} MHz", freq);
27+
unsafe { CPU_FREQ_MHZ = freq as u64 }
28+
}
29+
}
30+
31+
let lapic = local_apic();
32+
unsafe {
33+
lapic.set_timer_mode(TimerMode::Periodic);
34+
lapic.set_timer_divide(TimerDivide::Div256); // indeed it is Div1, the name is confusing.
35+
lapic.set_timer_initial((LAPIC_TICKS_PER_SEC / TICKS_PER_SEC) as u32);
36+
}
37+
}

0 commit comments

Comments
 (0)