Compare commits
7 Commits
52098ee0e9
...
v0.0.2
Author | SHA1 | Date | |
---|---|---|---|
ecad19bb5e | |||
94cbcd6a1f | |||
de21059b53 | |||
7fb000f5ce | |||
fc93be3043 | |||
4a44cdea8d | |||
a0a204b43c |
@ -5,6 +5,7 @@ use crate::{
|
||||
interrupt::{
|
||||
apic::{EOI, LAPIC},
|
||||
gdt,
|
||||
subscriber::InterruptContext,
|
||||
},
|
||||
time, warn,
|
||||
};
|
||||
@ -40,6 +41,17 @@ pub fn init_idt() {
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
||||
let context = InterruptContext {
|
||||
vector: 3,
|
||||
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
|
||||
code_segment: stack_frame.code_segment.0 as u64,
|
||||
cpu_flags: stack_frame.cpu_flags.bits(),
|
||||
stack_pointer: stack_frame.stack_pointer.as_u64(),
|
||||
stack_segment: stack_frame.stack_segment.0 as u64,
|
||||
};
|
||||
|
||||
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
|
||||
|
||||
warn!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||
}
|
||||
|
||||
@ -47,6 +59,17 @@ extern "x86-interrupt" fn double_fault_handler(
|
||||
stack_frame: InterruptStackFrame,
|
||||
_error_code: u64,
|
||||
) -> ! {
|
||||
let context = InterruptContext {
|
||||
vector: 8,
|
||||
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
|
||||
code_segment: stack_frame.code_segment.0 as u64,
|
||||
cpu_flags: stack_frame.cpu_flags.bits(),
|
||||
stack_pointer: stack_frame.stack_pointer.as_u64(),
|
||||
stack_segment: stack_frame.stack_segment.0 as u64,
|
||||
};
|
||||
|
||||
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
|
||||
|
||||
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||
}
|
||||
|
||||
@ -56,6 +79,17 @@ extern "x86-interrupt" fn page_fault_handler(
|
||||
) {
|
||||
use x86_64::registers::control::Cr2;
|
||||
|
||||
let context = InterruptContext {
|
||||
vector: 14,
|
||||
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
|
||||
code_segment: stack_frame.code_segment.0 as u64,
|
||||
cpu_flags: stack_frame.cpu_flags.bits(),
|
||||
stack_pointer: stack_frame.stack_pointer.as_u64(),
|
||||
stack_segment: stack_frame.stack_segment.0 as u64,
|
||||
};
|
||||
|
||||
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
|
||||
|
||||
panic!(
|
||||
"EXCEPTION: PAGE FAULT\n{:#?}\nAccessed address: {:#x}",
|
||||
stack_frame,
|
||||
@ -63,7 +97,18 @@ extern "x86-interrupt" fn page_fault_handler(
|
||||
);
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) {
|
||||
extern "x86-interrupt" fn timer_handler(stack_frame: InterruptStackFrame) {
|
||||
let context = InterruptContext {
|
||||
vector: IRQ_TIMER as u8,
|
||||
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
|
||||
code_segment: stack_frame.code_segment.0 as u64,
|
||||
cpu_flags: stack_frame.cpu_flags.bits(),
|
||||
stack_pointer: stack_frame.stack_pointer.as_u64(),
|
||||
stack_segment: stack_frame.stack_segment.0 as u64,
|
||||
};
|
||||
|
||||
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
|
||||
|
||||
time::tick();
|
||||
LAPIC.get().unwrap().write(EOI, 0);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod apic;
|
||||
pub mod gdt;
|
||||
pub mod idt;
|
||||
pub mod subscriber;
|
||||
|
68
nel_os_kernel/src/interrupt/subscriber.rs
Normal file
68
nel_os_kernel/src/interrupt/subscriber.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use spin::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptContext {
|
||||
pub vector: u8,
|
||||
pub instruction_pointer: u64,
|
||||
pub code_segment: u64,
|
||||
pub cpu_flags: u64,
|
||||
pub stack_pointer: u64,
|
||||
pub stack_segment: u64,
|
||||
}
|
||||
|
||||
pub type SubscriberCallback = fn(*mut core::ffi::c_void, &InterruptContext);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Subscriber {
|
||||
pub callback: SubscriberCallback,
|
||||
pub context: *mut core::ffi::c_void,
|
||||
}
|
||||
|
||||
unsafe impl Send for Subscriber {}
|
||||
unsafe impl Sync for Subscriber {}
|
||||
|
||||
const MAX_SUBSCRIBERS: usize = 10;
|
||||
|
||||
static SUBSCRIBERS: Mutex<[Option<Subscriber>; MAX_SUBSCRIBERS]> =
|
||||
Mutex::new([None; MAX_SUBSCRIBERS]);
|
||||
|
||||
pub fn subscribe(
|
||||
callback: SubscriberCallback,
|
||||
context: *mut core::ffi::c_void,
|
||||
) -> Result<(), &'static str> {
|
||||
let mut subscribers = SUBSCRIBERS.lock();
|
||||
|
||||
for slot in subscribers.iter_mut() {
|
||||
if slot.is_none() {
|
||||
*slot = Some(Subscriber { callback, context });
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err("No available subscriber slots")
|
||||
}
|
||||
|
||||
pub fn unsubscribe(callback: SubscriberCallback) -> Result<(), &'static str> {
|
||||
let mut subscribers = SUBSCRIBERS.lock();
|
||||
|
||||
for slot in subscribers.iter_mut() {
|
||||
if let Some(subscriber) = slot {
|
||||
if core::ptr::fn_addr_eq(subscriber.callback, callback) {
|
||||
*slot = None;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err("Subscriber not found")
|
||||
}
|
||||
|
||||
pub fn dispatch_to_subscribers(context: &InterruptContext) {
|
||||
let subscribers = SUBSCRIBERS.lock();
|
||||
|
||||
for subscriber in subscribers.iter() {
|
||||
if let Some(subscriber) = subscriber {
|
||||
(subscriber.callback)(subscriber.context, context);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
use crate::vmm::x86_64::{common, intel::vmcs};
|
||||
use crate::vmm::x86_64::{
|
||||
common,
|
||||
intel::{vmcs, vmwrite},
|
||||
};
|
||||
|
||||
pub fn setup_exec_controls() -> Result<(), &'static str> {
|
||||
let basic_msr = common::read_msr(0x480);
|
||||
@ -12,8 +15,8 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
|
||||
raw_pin_exec_ctrl |= (reserved_bits & 0xFFFFFFFF) as u32;
|
||||
raw_pin_exec_ctrl &= (reserved_bits >> 32) as u32;
|
||||
|
||||
let pin_exec_ctrl = vmcs::controls::PinBasedVmExecutionControls::from(raw_pin_exec_ctrl);
|
||||
//pin_exec_ctrl.set_external_interrupt_exiting(false);
|
||||
let mut pin_exec_ctrl = vmcs::controls::PinBasedVmExecutionControls::from(raw_pin_exec_ctrl);
|
||||
pin_exec_ctrl.set_external_interrupt_exiting(false);
|
||||
|
||||
pin_exec_ctrl.write()?;
|
||||
|
||||
@ -33,6 +36,8 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
|
||||
primary_exec_ctrl.set_hlt(true);
|
||||
primary_exec_ctrl.set_activate_secondary_controls(true);
|
||||
primary_exec_ctrl.set_use_msr_bitmap(false);
|
||||
primary_exec_ctrl.set_unconditional_io(false);
|
||||
primary_exec_ctrl.set_use_io_bitmap(true);
|
||||
|
||||
primary_exec_ctrl.write()?;
|
||||
|
||||
@ -55,6 +60,9 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
|
||||
|
||||
secondary_exec_ctrl.write()?;
|
||||
|
||||
vmwrite(x86::vmx::vmcs::control::CR0_GUEST_HOST_MASK, u64::MAX)?;
|
||||
vmwrite(x86::vmx::vmcs::control::CR4_GUEST_HOST_MASK, u64::MAX)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -101,7 +109,13 @@ pub fn setup_exit_controls() -> Result<(), &'static str> {
|
||||
|
||||
exit_ctrl.write()?;
|
||||
|
||||
//vmwrite(0x4004, 1u64 << 6)?; // EXCEPTION_BITMAP
|
||||
/*vmwrite(
|
||||
0x4004,
|
||||
1u64 << x86::irq::DOUBLE_FAULT_VECTOR
|
||||
| 1u64 << x86::irq::GENERAL_PROTECTION_FAULT_VECTOR
|
||||
| 1u64 << x86::irq::PAGE_FAULT_VECTOR
|
||||
| 1u64 << x86::irq::X87_FPU_VECTOR,
|
||||
)?;*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
166
nel_os_kernel/src/vmm/x86_64/intel/cr.rs
Normal file
166
nel_os_kernel/src/vmm/x86_64/intel/cr.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use x86::vmx::vmcs;
|
||||
|
||||
use crate::vmm::x86_64::{
|
||||
common::read_msr,
|
||||
intel::{
|
||||
qual::{AccessType, QualCr, Register},
|
||||
vcpu::IntelVCpu,
|
||||
vmread, vmwrite,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn handle_cr_access(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<(), &'static str> {
|
||||
match qual.access_type() {
|
||||
AccessType::MovTo => match qual.index() {
|
||||
0 | 4 => {
|
||||
passthrough_write(vcpu, qual)?;
|
||||
update_ia32e(vcpu)?;
|
||||
}
|
||||
_ => panic!("Unsupported CR index: {}", qual.index()),
|
||||
},
|
||||
AccessType::MovFrom => passthrough_read(vcpu, qual)?,
|
||||
_ => {
|
||||
panic!("Unsupported CR access type: {:?}", qual.access_type());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn passthrough_read(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<(), &'static str> {
|
||||
let value = match qual.index() {
|
||||
3 => vmread(x86::vmx::vmcs::guest::CR3)?,
|
||||
_ => panic!("Unsupported CR index: {}", qual.index()),
|
||||
};
|
||||
|
||||
set_value(vcpu, qual, value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn passthrough_write(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<(), &'static str> {
|
||||
let value = get_value(vcpu, qual)?;
|
||||
match qual.index() {
|
||||
0 => {
|
||||
vmwrite(vmcs::guest::CR0, adjust_cr0(value))?;
|
||||
vmwrite(vmcs::control::CR0_READ_SHADOW, value)?;
|
||||
}
|
||||
4 => {
|
||||
vmwrite(vmcs::guest::CR4, adjust_cr4(value))?;
|
||||
vmwrite(vmcs::control::CR4_READ_SHADOW, value)?;
|
||||
}
|
||||
_ => {
|
||||
panic!("Unsupported CR index: {}", qual.index());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_ia32e(vcpu: &mut IntelVCpu) -> Result<(), &'static str> {
|
||||
let cr0 = vmread(x86::vmx::vmcs::guest::CR0)?;
|
||||
let cr4 = vmread(x86::vmx::vmcs::guest::CR4)?;
|
||||
let ia32e_enabled = (cr0 & 1 << 31) != 0 && (cr4 & 1 << 5) != 0;
|
||||
|
||||
vcpu.ia32e_enabled = ia32e_enabled;
|
||||
|
||||
let mut entry_ctrl = super::vmcs::controls::EntryControls::read()?;
|
||||
entry_ctrl.set_ia32e_mode_guest(ia32e_enabled);
|
||||
entry_ctrl.write()?;
|
||||
|
||||
let mut efer = vmread(x86::vmx::vmcs::guest::IA32_EFER_FULL)?;
|
||||
|
||||
let lma = (vcpu.ia32e_enabled as u64) << 10;
|
||||
if lma != 0 {
|
||||
efer |= lma;
|
||||
} else {
|
||||
efer &= !lma;
|
||||
}
|
||||
|
||||
let lme = if cr0 & (1 << 31) != 0 {
|
||||
efer & (1 << 10)
|
||||
} else {
|
||||
efer & !(1 << 8)
|
||||
};
|
||||
if lme != 0 {
|
||||
efer |= lme;
|
||||
} else {
|
||||
efer &= lme;
|
||||
}
|
||||
|
||||
vmwrite(x86::vmx::vmcs::guest::IA32_EFER_FULL, efer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn adjust_cr0(value: u64) -> u64 {
|
||||
let mut result = value;
|
||||
|
||||
let cr0_fixed0 = read_msr(x86::msr::IA32_VMX_CR0_FIXED0);
|
||||
let cr0_fixed1 = read_msr(x86::msr::IA32_VMX_CR0_FIXED1);
|
||||
|
||||
result |= cr0_fixed0;
|
||||
result &= cr0_fixed1;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn adjust_cr4(value: u64) -> u64 {
|
||||
let mut result = value;
|
||||
|
||||
let cr4_fixed0 = read_msr(x86::msr::IA32_VMX_CR4_FIXED0);
|
||||
let cr4_fixed1 = read_msr(x86::msr::IA32_VMX_CR4_FIXED1);
|
||||
|
||||
result |= cr4_fixed0;
|
||||
result &= cr4_fixed1;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn set_value(vcpu: &mut IntelVCpu, qual: &QualCr, value: u64) -> Result<(), &'static str> {
|
||||
let guest_regs = &mut vcpu.guest_registers;
|
||||
|
||||
match qual.register() {
|
||||
Register::Rax => guest_regs.rax = value,
|
||||
Register::Rcx => guest_regs.rcx = value,
|
||||
Register::Rdx => guest_regs.rdx = value,
|
||||
Register::Rbx => guest_regs.rbx = value,
|
||||
Register::Rbp => guest_regs.rbp = value,
|
||||
Register::Rsi => guest_regs.rsi = value,
|
||||
Register::Rdi => guest_regs.rdi = value,
|
||||
Register::R8 => guest_regs.r8 = value,
|
||||
Register::R9 => guest_regs.r9 = value,
|
||||
Register::R10 => guest_regs.r10 = value,
|
||||
Register::R11 => guest_regs.r11 = value,
|
||||
Register::R12 => guest_regs.r12 = value,
|
||||
Register::R13 => guest_regs.r13 = value,
|
||||
Register::R14 => guest_regs.r14 = value,
|
||||
Register::R15 => guest_regs.r15 = value,
|
||||
Register::Rsp => vmwrite(x86::vmx::vmcs::guest::RSP, value)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_value(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<u64, &'static str> {
|
||||
let guest_regs = &mut vcpu.guest_registers;
|
||||
|
||||
Ok(match qual.register() {
|
||||
Register::Rax => guest_regs.rax,
|
||||
Register::Rcx => guest_regs.rcx,
|
||||
Register::Rdx => guest_regs.rdx,
|
||||
Register::Rbx => guest_regs.rbx,
|
||||
Register::Rbp => guest_regs.rbp,
|
||||
Register::Rsi => guest_regs.rsi,
|
||||
Register::Rdi => guest_regs.rdi,
|
||||
Register::R8 => guest_regs.r8,
|
||||
Register::R9 => guest_regs.r9,
|
||||
Register::R10 => guest_regs.r10,
|
||||
Register::R11 => guest_regs.r11,
|
||||
Register::R12 => guest_regs.r12,
|
||||
Register::R13 => guest_regs.r13,
|
||||
Register::R14 => guest_regs.r14,
|
||||
Register::R15 => guest_regs.r15,
|
||||
Register::Rsp => vmread(x86::vmx::vmcs::guest::RSP)?,
|
||||
})
|
||||
}
|
249
nel_os_kernel/src/vmm/x86_64/intel/io.rs
Normal file
249
nel_os_kernel/src/vmm/x86_64/intel/io.rs
Normal file
@ -0,0 +1,249 @@
|
||||
use x86::vmx::vmcs;
|
||||
use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB};
|
||||
|
||||
use super::qual::QualIo;
|
||||
use crate::{
|
||||
info,
|
||||
vmm::x86_64::intel::{register::GuestRegisters, vmwrite},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InitPhase {
|
||||
Uninitialized,
|
||||
Phase1,
|
||||
Phase2,
|
||||
Phase3,
|
||||
Initialized,
|
||||
}
|
||||
|
||||
pub enum ReadSel {
|
||||
IRR,
|
||||
ISR,
|
||||
}
|
||||
|
||||
pub struct PIC {
|
||||
pub primary_mask: u8,
|
||||
pub secondary_mask: u8,
|
||||
pub primary_phase: InitPhase,
|
||||
pub secondary_phase: InitPhase,
|
||||
pub primary_base: u8,
|
||||
pub secondary_base: u8,
|
||||
pub primary_irr: u8,
|
||||
pub primary_isr: u8,
|
||||
pub secondary_irr: u8,
|
||||
pub secondary_isr: u8,
|
||||
pub primary_read_sel: ReadSel,
|
||||
pub secondary_read_sel: ReadSel,
|
||||
}
|
||||
|
||||
impl PIC {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
primary_mask: 0xFF,
|
||||
secondary_mask: 0xFF,
|
||||
primary_phase: InitPhase::Uninitialized,
|
||||
secondary_phase: InitPhase::Uninitialized,
|
||||
primary_base: 0,
|
||||
secondary_base: 0,
|
||||
primary_irr: 0,
|
||||
primary_isr: 0,
|
||||
secondary_irr: 0,
|
||||
secondary_isr: 0,
|
||||
primary_read_sel: ReadSel::IRR,
|
||||
secondary_read_sel: ReadSel::IRR,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_io(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.direction() {
|
||||
0 => {
|
||||
self.handle_io_out(regs, qual);
|
||||
}
|
||||
1 => {
|
||||
self.handle_io_in(regs, qual);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_io_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.port() {
|
||||
0x0CF8..=0x0CFF => regs.rax = 0,
|
||||
0xC000..=0xCFFF => {} //ignore
|
||||
0x20..=0x21 => self.handle_pic_in(regs, qual),
|
||||
0xA0..=0xA1 => self.handle_pic_in(regs, qual),
|
||||
0x0070..=0x0071 => regs.rax = 0,
|
||||
_ => regs.rax = 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_io_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.port() {
|
||||
0x0CF8..=0x0CFF => {} //ignore
|
||||
0xC000..=0xCFFF => {} //ignore
|
||||
0x20..=0x21 => self.handle_pic_out(regs, qual),
|
||||
0xA0..=0xA1 => self.handle_pic_out(regs, qual),
|
||||
0x0070..=0x0071 => {} //ignore
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_pic_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
match qual.port() {
|
||||
0x20 => {
|
||||
let v = match self.primary_read_sel {
|
||||
ReadSel::IRR => self.primary_irr,
|
||||
ReadSel::ISR => self.primary_isr,
|
||||
};
|
||||
regs.rax = v as u64;
|
||||
}
|
||||
0xA0 => {
|
||||
let v = match self.secondary_read_sel {
|
||||
ReadSel::IRR => self.secondary_irr,
|
||||
ReadSel::ISR => self.secondary_isr,
|
||||
};
|
||||
regs.rax = v as u64;
|
||||
}
|
||||
0x21 => match self.primary_phase {
|
||||
InitPhase::Uninitialized | InitPhase::Initialized => {
|
||||
regs.rax = self.primary_mask as u64;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
0xA1 => match self.secondary_phase {
|
||||
InitPhase::Uninitialized | InitPhase::Initialized => {
|
||||
regs.rax = self.secondary_mask as u64;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_pic_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
|
||||
let pic = self;
|
||||
let dx = regs.rax as u8;
|
||||
match qual.port() {
|
||||
0x20 => match dx {
|
||||
0x11 => pic.primary_phase = InitPhase::Phase1,
|
||||
0x0A => pic.primary_read_sel = ReadSel::ISR,
|
||||
0x0B => pic.primary_read_sel = ReadSel::IRR,
|
||||
0x20 => {
|
||||
pic.primary_isr = 0;
|
||||
}
|
||||
0x60..=0x67 => {
|
||||
let irq = dx & 0x7;
|
||||
pic.primary_isr &= !(1 << irq);
|
||||
}
|
||||
_ => panic!("Primary PIC command: {:#x}", dx),
|
||||
},
|
||||
0x21 => match pic.primary_phase {
|
||||
InitPhase::Uninitialized | InitPhase::Initialized => pic.primary_mask = dx,
|
||||
InitPhase::Phase1 => {
|
||||
pic.primary_base = dx;
|
||||
pic.primary_phase = InitPhase::Phase2;
|
||||
}
|
||||
InitPhase::Phase2 => {
|
||||
pic.primary_phase = InitPhase::Phase3;
|
||||
}
|
||||
InitPhase::Phase3 => {
|
||||
info!("Primary PIC Initialized");
|
||||
pic.primary_phase = InitPhase::Initialized
|
||||
}
|
||||
},
|
||||
0xA0 => match dx {
|
||||
0x11 => pic.secondary_phase = InitPhase::Phase1,
|
||||
0x0A => pic.secondary_read_sel = ReadSel::ISR,
|
||||
0x0B => pic.secondary_read_sel = ReadSel::IRR,
|
||||
0x20 => {
|
||||
pic.secondary_isr = 0;
|
||||
}
|
||||
0x60..=0x67 => {
|
||||
let irq = dx & 0x7;
|
||||
pic.secondary_isr &= !(1 << irq);
|
||||
}
|
||||
_ => panic!("Secondary PIC command: {:#x}", dx),
|
||||
},
|
||||
0xA1 => match pic.secondary_phase {
|
||||
InitPhase::Uninitialized | InitPhase::Initialized => pic.secondary_mask = dx,
|
||||
InitPhase::Phase1 => {
|
||||
pic.secondary_base = dx;
|
||||
pic.secondary_phase = InitPhase::Phase2;
|
||||
}
|
||||
InitPhase::Phase2 => {
|
||||
pic.secondary_phase = InitPhase::Phase3;
|
||||
}
|
||||
InitPhase::Phase3 => {
|
||||
info!("Secondary PIC Initialized");
|
||||
pic.secondary_phase = InitPhase::Initialized
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IOBitmap {
|
||||
pub bitmap_a: PhysFrame,
|
||||
pub bitmap_b: PhysFrame,
|
||||
}
|
||||
|
||||
impl IOBitmap {
|
||||
pub fn new(frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Self {
|
||||
let bitmap_a = frame_allocator
|
||||
.allocate_frame()
|
||||
.expect("Failed to allocate I/O bitmap A");
|
||||
let bitmap_b = frame_allocator
|
||||
.allocate_frame()
|
||||
.expect("Failed to allocate I/O bitmap B");
|
||||
|
||||
Self { bitmap_a, bitmap_b }
|
||||
}
|
||||
|
||||
pub fn setup(&mut self) -> Result<(), &'static str> {
|
||||
let bitmap_a_addr = self.bitmap_a.start_address().as_u64() as usize;
|
||||
let bitmap_b_addr = self.bitmap_b.start_address().as_u64() as usize;
|
||||
|
||||
unsafe {
|
||||
core::ptr::write_bytes(bitmap_a_addr as *mut u8, u8::MAX, 4096);
|
||||
core::ptr::write_bytes(bitmap_b_addr as *mut u8, u8::MAX, 4096);
|
||||
}
|
||||
|
||||
self.set_io_ports(0x02F8..=0x03FF);
|
||||
self.set_io_ports(0x0040..=0x0047);
|
||||
|
||||
vmwrite(vmcs::control::IO_BITMAP_A_ADDR_FULL, bitmap_a_addr as u64)?;
|
||||
vmwrite(vmcs::control::IO_BITMAP_B_ADDR_FULL, bitmap_b_addr as u64)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_io_ports(&mut self, ports: core::ops::RangeInclusive<u16>) {
|
||||
for port in ports {
|
||||
if port <= 0x7FFF {
|
||||
let byte_index = port as usize / 8;
|
||||
let bit_index = port as usize % 8;
|
||||
|
||||
self.get_bitmap_a()[byte_index] &= !(1 << bit_index);
|
||||
} else {
|
||||
let adjusted_port = port - 0x8000;
|
||||
let byte_index = adjusted_port as usize / 8;
|
||||
let bit_index = adjusted_port as usize % 8;
|
||||
|
||||
self.get_bitmap_b()[byte_index] &= !(1 << bit_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bitmap_a(&self) -> &mut [u8] {
|
||||
unsafe {
|
||||
core::slice::from_raw_parts_mut(self.bitmap_a.start_address().as_u64() as *mut u8, 4096)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bitmap_b(&self) -> &mut [u8] {
|
||||
unsafe {
|
||||
core::slice::from_raw_parts_mut(self.bitmap_b.start_address().as_u64() as *mut u8, 4096)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,11 @@ pub mod asm;
|
||||
mod auditor;
|
||||
mod controls;
|
||||
mod cpuid;
|
||||
mod cr;
|
||||
mod ept;
|
||||
mod io;
|
||||
mod msr;
|
||||
mod qual;
|
||||
mod register;
|
||||
pub mod vcpu;
|
||||
mod vmcs;
|
||||
|
@ -92,7 +92,7 @@ pub fn register_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
|
||||
pub fn _update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
|
||||
info!("updating MSRs");
|
||||
let indices_to_update: alloc::vec::Vec<u32> = vcpu
|
||||
.host_msr
|
||||
@ -101,8 +101,6 @@ pub fn update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
|
||||
.map(|entry| entry.index)
|
||||
.collect();
|
||||
|
||||
info!("1");
|
||||
|
||||
for index in indices_to_update {
|
||||
info!("{}", index);
|
||||
let value = read_msr(index);
|
||||
@ -110,25 +108,21 @@ pub fn update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
|
||||
vcpu.host_msr.set_by_index(index, value).unwrap();
|
||||
}
|
||||
|
||||
info!("2");
|
||||
vmwrite(
|
||||
vmcs::control::VMEXIT_MSR_LOAD_COUNT,
|
||||
vcpu.host_msr.saved_ents().len() as u64,
|
||||
)
|
||||
.unwrap();
|
||||
info!("3");
|
||||
vmwrite(
|
||||
vmcs::control::VMEXIT_MSR_STORE_COUNT,
|
||||
vcpu.guest_msr.saved_ents().len() as u64,
|
||||
)
|
||||
.unwrap();
|
||||
info!("4");
|
||||
vmwrite(
|
||||
vmcs::control::VMENTRY_MSR_LOAD_COUNT,
|
||||
vcpu.guest_msr.saved_ents().len() as u64,
|
||||
)
|
||||
.unwrap();
|
||||
info!("5");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
124
nel_os_kernel/src/vmm/x86_64/intel/qual.rs
Normal file
124
nel_os_kernel/src/vmm/x86_64/intel/qual.rs
Normal file
@ -0,0 +1,124 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::Debug;
|
||||
|
||||
use modular_bitfield::prelude::{B1, B16, B32, B4, B8};
|
||||
use modular_bitfield::{bitfield, Specifier};
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Specifier, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AccessType {
|
||||
MovTo = 0,
|
||||
MovFrom = 1,
|
||||
Clts = 2,
|
||||
Lmsw = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for AccessType {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(AccessType::MovTo),
|
||||
1 => Ok(AccessType::MovFrom),
|
||||
2 => Ok(AccessType::Clts),
|
||||
3 => Ok(AccessType::Lmsw),
|
||||
_ => Err("Invalid AccessType value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Specifier, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LmswOperandType {
|
||||
Reg = 0,
|
||||
Mem = 1,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for LmswOperandType {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(LmswOperandType::Reg),
|
||||
1 => Ok(LmswOperandType::Mem),
|
||||
_ => Err("Invalid LmswOperandType value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Specifier, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Register {
|
||||
Rax = 0,
|
||||
Rcx = 1,
|
||||
Rdx = 2,
|
||||
Rbx = 3,
|
||||
Rsp = 4,
|
||||
Rbp = 5,
|
||||
Rsi = 6,
|
||||
Rdi = 7,
|
||||
R8 = 8,
|
||||
R9 = 9,
|
||||
R10 = 10,
|
||||
R11 = 11,
|
||||
R12 = 12,
|
||||
R13 = 13,
|
||||
R14 = 14,
|
||||
R15 = 15,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Register {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Register::Rax),
|
||||
1 => Ok(Register::Rcx),
|
||||
2 => Ok(Register::Rdx),
|
||||
3 => Ok(Register::Rbx),
|
||||
4 => Ok(Register::Rsp),
|
||||
5 => Ok(Register::Rbp),
|
||||
6 => Ok(Register::Rsi),
|
||||
7 => Ok(Register::Rdi),
|
||||
8 => Ok(Register::R8),
|
||||
9 => Ok(Register::R9),
|
||||
10 => Ok(Register::R10),
|
||||
11 => Ok(Register::R11),
|
||||
12 => Ok(Register::R12),
|
||||
13 => Ok(Register::R13),
|
||||
14 => Ok(Register::R14),
|
||||
15 => Ok(Register::R15),
|
||||
_ => Err("Invalid Register value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct QualCr {
|
||||
pub index: B4,
|
||||
#[bits = 2]
|
||||
pub access_type: AccessType,
|
||||
#[bits = 1]
|
||||
pub lmsw_operand_type: LmswOperandType,
|
||||
_reserved1: B1,
|
||||
#[bits = 4]
|
||||
pub register: Register,
|
||||
_reserved2: B4,
|
||||
pub lmsw_source: B16,
|
||||
_reseved3: B32,
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct QualIo {
|
||||
pub size: B4,
|
||||
pub direction: B1,
|
||||
pub string: B1,
|
||||
pub rep: B1,
|
||||
pub operand_encoding: B1,
|
||||
_reserved1: B8,
|
||||
pub port: B16,
|
||||
_reserved2: B32,
|
||||
}
|
@ -12,7 +12,9 @@ use crate::{
|
||||
common::{self, read_msr},
|
||||
intel::{
|
||||
auditor, controls, cpuid, ept,
|
||||
io::IOBitmap,
|
||||
msr::{self, ShadowMsr},
|
||||
qual::{QualCr, QualIo},
|
||||
register::GuestRegisters,
|
||||
vmcs::{
|
||||
self,
|
||||
@ -42,6 +44,9 @@ pub struct IntelVCpu {
|
||||
guest_memory_size: u64,
|
||||
pub host_msr: ShadowMsr,
|
||||
pub guest_msr: ShadowMsr,
|
||||
pub ia32e_enabled: bool,
|
||||
pic: super::io::PIC,
|
||||
io_bitmap: IOBitmap,
|
||||
}
|
||||
|
||||
impl IntelVCpu {
|
||||
@ -76,7 +81,6 @@ impl IntelVCpu {
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
VmxExitReason::CPUID => {
|
||||
info!("VM exit reason: CPUID");
|
||||
cpuid::handle_cpuid_vmexit(self);
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
@ -88,11 +92,53 @@ impl IntelVCpu {
|
||||
msr::ShadowMsr::handle_wrmsr_vmexit(self);
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
VmxExitReason::CONTROL_REGISTER_ACCESSES => {
|
||||
let qual = vmread(vmcs::ro::EXIT_QUALIFICATION)?;
|
||||
let qual = QualCr::from(qual);
|
||||
|
||||
super::cr::handle_cr_access(self, &qual)?;
|
||||
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
VmxExitReason::IO_INSTRUCTION => {
|
||||
let qual = vmread(vmcs::ro::EXIT_QUALIFICATION)?;
|
||||
let qual_io = QualIo::from(qual);
|
||||
|
||||
self.pic.handle_io(&mut self.guest_registers, qual_io);
|
||||
|
||||
self.step_next_inst()?;
|
||||
}
|
||||
VmxExitReason::EPT_VIOLATION => {
|
||||
let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?;
|
||||
info!("EPT Violation at guest address: {:#x}", guest_address);
|
||||
return Err("EPT Violation");
|
||||
}
|
||||
VmxExitReason::TRIPLE_FAULT => {
|
||||
info!("Triple fault detected");
|
||||
return Err("Triple fault");
|
||||
}
|
||||
VmxExitReason::EXCEPTION => {
|
||||
let vmexit_intr_info = vmread(vmcs::ro::VMEXIT_INTERRUPTION_INFO)?;
|
||||
let vector = (vmexit_intr_info & 0xFF) as u8;
|
||||
let error_code = (vmexit_intr_info >> 8) & 0b111;
|
||||
let error_code_valid = (vmexit_intr_info >> 11) & 0b1 != 0;
|
||||
|
||||
let idt_vectoring_info = vmread(vmcs::ro::IDT_VECTORING_INFO)?;
|
||||
info!("idt valid: {}", idt_vectoring_info >> 31 & 0b1 != 0);
|
||||
|
||||
let rip = vmread(vmcs::guest::RIP)?;
|
||||
let hpa = self.ept.get_phys_addr(rip).unwrap();
|
||||
|
||||
if error_code_valid {
|
||||
info!(
|
||||
"VM exit due to exception: vector {}, error code {}, at RIP {:#x} (hpa: {:#x})",
|
||||
vector, error_code, rip, hpa
|
||||
);
|
||||
} else {
|
||||
info!("VM exit due to exception: vector {}", vector);
|
||||
}
|
||||
return Err("VM exit due to exception");
|
||||
}
|
||||
_ => {
|
||||
info!("VM exit reason: {:?}", exit_reason);
|
||||
return Err("Unhandled VM exit reason");
|
||||
@ -114,8 +160,6 @@ impl IntelVCpu {
|
||||
}
|
||||
|
||||
fn vmentry(&mut self) -> Result<(), InstructionError> {
|
||||
msr::update_msrs(self).unwrap();
|
||||
|
||||
auditor::controls::check_vmcs_control_fields().unwrap();
|
||||
|
||||
let success = {
|
||||
@ -152,6 +196,7 @@ impl IntelVCpu {
|
||||
controls::setup_exit_controls()?;
|
||||
Self::setup_host_state()?;
|
||||
self.setup_guest_state()?;
|
||||
self.io_bitmap.setup()?;
|
||||
|
||||
self.init_guest_memory(frame_allocator)?;
|
||||
|
||||
@ -233,14 +278,18 @@ impl IntelVCpu {
|
||||
|
||||
fn setup_guest_state(&mut self) -> Result<(), &'static str> {
|
||||
use x86::{controlregs::*, vmx::vmcs};
|
||||
let cr0 = Cr0::empty()
|
||||
let cr0 = (Cr0::empty()
|
||||
| Cr0::CR0_PROTECTED_MODE
|
||||
| Cr0::CR0_NUMERIC_ERROR & !Cr0::CR0_ENABLE_PAGING;
|
||||
| Cr0::CR0_NUMERIC_ERROR
|
||||
| Cr0::CR0_EXTENSION_TYPE)
|
||||
& !Cr0::CR0_ENABLE_PAGING;
|
||||
vmwrite(vmcs::guest::CR0, cr0.bits() as u64)?;
|
||||
vmwrite(vmcs::guest::CR3, unsafe { cr3() })?;
|
||||
vmwrite(vmcs::guest::CR3, 0)?;
|
||||
vmwrite(
|
||||
vmcs::guest::CR4,
|
||||
vmread(vmcs::guest::CR4)? & !Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits(),
|
||||
vmread(vmcs::guest::CR4)?
|
||||
| Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits()
|
||||
& !Cr4Flags::PHYSICAL_ADDRESS_EXTENSION.bits(),
|
||||
)?;
|
||||
|
||||
vmwrite(vmcs::guest::CS_BASE, 0)?;
|
||||
@ -638,6 +687,9 @@ impl VCpu for IntelVCpu {
|
||||
guest_memory_size: 1024 * 1024 * 256, // 256 MiB
|
||||
host_msr: ShadowMsr::new(),
|
||||
guest_msr: ShadowMsr::new(),
|
||||
ia32e_enabled: false,
|
||||
pic: super::io::PIC::new(),
|
||||
io_bitmap: IOBitmap::new(frame_allocator),
|
||||
})
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user