diff --git a/Makefile b/Makefile index 11f06a7..4f9df2f 100644 --- a/Makefile +++ b/Makefile @@ -5,11 +5,13 @@ MODE ?= release ARCH ?= x86_64 MEMORY ?= 512M # In MB -ISO_SIZE ?= 256 +ISO_SIZE ?= 512 QEMU_OPTS ?= MKSQUASHFS_OPTS ?= GDB ?= -ESP_BITS ?= 16 +CPUS ?= 1 +# FAT type +ESP_BITS ?= 32 ISO_PATH = ${ARTIFACTS_PATH}/iso_root INITRAMFS_PATH = ${ARTIFACTS_PATH}/initramfs @@ -22,6 +24,10 @@ ifeq (${MODE},release) CARGO_OPTS += --release endif +ifneq (${CPUS},1) + QEMU_OPTS += -smp ${CPUS} +endif + ifneq (${GDB},) QEMU_OPTS += -s -S endif @@ -118,16 +124,16 @@ partition-iso: copy-iso-files dd if=/dev/zero of=${IMAGE_PATH} bs=1M count=0 seek=${ISO_SIZE} ifeq (${ISO_PARTITION_TYPE},GPT) parted -s ${IMAGE_PATH} mklabel gpt - parted -s ${IMAGE_PATH} mkpart ESP fat${ESP_BITS} 2048s 34815s + parted -s ${IMAGE_PATH} mkpart ESP fat${ESP_BITS} 2048s 262144s else parted -s ${IMAGE_PATH} mklabel msdos - parted -s ${IMAGE_PATH} mkpart primary fat${ESP_BITS} 2048s 34815s + parted -s ${IMAGE_PATH} mkpart primary fat${ESP_BITS} 2048s 262144s endif # Make ISO with 1 partition starting at sector 2048 that is 32768 sectors, or 16MiB, in size # Then a second partition spanning the rest of the disk - parted -s ${IMAGE_PATH} mkpart primary 34816s 100% + parted -s ${IMAGE_PATH} mkpart primary 262145s 100% parted -s ${IMAGE_PATH} set 1 esp on build-iso: partition-iso copy-initramfs-files diff --git a/limine.cfg b/limine.cfg index d290387..37bfe9c 100644 --- a/limine.cfg +++ b/limine.cfg @@ -7,4 +7,4 @@ TIMEOUT=3 KERNEL_PATH=boot:///boot/CappuccinOS.elf KERNEL_CMDLINE=fat_in_mem=false big_fat_phony - MODULE_PATH=boot:///boot/initramfs.img \ No newline at end of file + MODULE_PATH=boot:///boot/initramfs.img diff --git a/src/arch/x86_64/interrupts/exceptions.rs b/src/arch/x86_64/interrupts/exceptions.rs index 6cf35b3..85859cc 100644 --- a/src/arch/x86_64/interrupts/exceptions.rs +++ b/src/arch/x86_64/interrupts/exceptions.rs @@ -34,8 +34,6 @@ struct Registers { } extern "C" fn exception_handler(registers: u64) { - crate::println!("{:X?}", registers); - let registers = unsafe { *(registers as *const Registers) }; crate::println!("{:X?}", registers); diff --git a/src/arch/x86_64/interrupts/mod.rs b/src/arch/x86_64/interrupts/mod.rs index f8525aa..bd38b80 100755 --- a/src/arch/x86_64/interrupts/mod.rs +++ b/src/arch/x86_64/interrupts/mod.rs @@ -1,6 +1,9 @@ mod exceptions; -use crate::{arch::x86_common::pic::ChainedPics, libs::mutex::Mutex}; +use crate::{ + arch::{apic, x86_common::pic::ChainedPics}, + libs::mutex::Mutex, +}; #[repr(C, packed)] #[derive(Clone, Copy)] @@ -74,16 +77,24 @@ pub fn idt_set_gate(num: u8, function_ptr: usize) { // If the interrupt with this number occurred with the "null" interrupt handler // We will need to tell the PIC that interrupt is over, this stops new interrupts // From never firing because "it was never finished" - PICS.lock().write().notify_end_of_interrupt(num); + signal_end_of_interrupt(num); } -extern "x86-interrupt" fn null_interrupt_handler() {} +extern "x86-interrupt" fn null_interrupt_handler() { + crate::log_info!("Unhandled interrupt!"); + if apic::APIC.lock().read().is_some() { + apic::APIC + .lock() + .read() + .as_ref() + .unwrap() + .end_of_interrupt(); + } +} extern "x86-interrupt" fn timer_handler() { // crate::usr::tty::puts("."); - PICS.lock() - .write() - .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); + signal_end_of_interrupt(InterruptIndex::Timer.as_u8()); } fn idt_init() { @@ -114,6 +125,19 @@ fn idt_init() { } } +pub fn signal_end_of_interrupt(int: u8) { + if apic::APIC.lock().read().is_some() { + apic::APIC + .lock() + .read() + .as_ref() + .unwrap() + .end_of_interrupt(); + } else { + PICS.lock().write().notify_end_of_interrupt(int); + } +} + #[naked] pub extern "C" fn syscall() { unsafe { diff --git a/src/arch/x86_common/apic.rs b/src/arch/x86_common/apic.rs new file mode 100644 index 0000000..d21ac80 --- /dev/null +++ b/src/arch/x86_common/apic.rs @@ -0,0 +1,211 @@ +use core::sync::atomic::AtomicBool; + +use alloc::{boxed::Box, sync::Arc, vec::Vec}; + +use crate::{drivers::acpi::SDTHeader, libs::mutex::Mutex}; + +use super::{cpu_get_msr, cpu_set_msr, pic::ChainedPics}; + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +struct MADT { + pub local_apic_address: u32, + pub flags: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +struct MADTEntry {} + +impl MADT { + pub fn as_ptr(&self) -> *const u8 { + core::ptr::addr_of!(self).cast::() + } +} + +const IA32_APIC_BASE_MSR: u32 = 0x1B; +const IA32_APIC_BASE_MSR_ENABLE: usize = 0x800; + +pub fn has_apic() -> bool { + return unsafe { core::arch::x86_64::__cpuid_count(1, 0).edx } & 1 << 9 != 0; +} + +fn set_apic_base(apic: usize) { + let edx: u32 = 0; + let eax = (apic & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE; + + unsafe { cpu_set_msr(IA32_APIC_BASE_MSR, &(eax as u32), &edx) }; +} + +fn get_apic_base() -> u32 { + let mut eax: u32 = 0; + let mut edx: u32 = 0; + unsafe { cpu_get_msr(IA32_APIC_BASE_MSR, &mut eax, &mut edx) }; + + return eax & 0xfffff000; +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +pub struct LAPIC { + pub acpi_processor_id: u8, + pub apic_id: u8, + pub flags: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +pub struct IOAPIC { + pub ioapic_id: u8, + _reserved: u8, + pub ptr: *mut u8, + pub global_interrupt_base: u32, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +pub struct IOAPICSourceOverride { + bus_source: u8, + irq_source: u8, + global_system_interrupt: u32, + flags: u16, +} + +#[derive(Debug)] +pub struct APIC { + pub io_apic: IOAPIC, + local_apic: *mut u8, + pub cpus: Arc<[LAPIC]>, +} + +impl APIC { + pub fn new() -> Result { + if !has_apic() { + return Err(()); + } + + let apic_base = get_apic_base() as usize; + + set_apic_base(apic_base); + + let madt = crate::drivers::acpi::find_table::("APIC"); + + if madt.is_none() { + return Err(()); + } + + let mut cpus: Vec = Vec::new(); + + let madt = madt.unwrap(); + + crate::log_info!("MADT located at: {:p}", core::ptr::addr_of!(madt)); + + let mut lapic_ptr = madt.inner.local_apic_address as *mut u8; + let mut io_apic = None; + let mut io_apic_source_override = None; + + let mut ptr = madt.extra.unwrap().as_ptr(); + let ptr_end = unsafe { ptr.add(madt.header.length as usize - 44) }; + + while (ptr as usize) < (ptr_end as usize) { + match unsafe { *ptr } { + 0 => { + if unsafe { *(ptr.add(4)) } & 1 != 0 { + cpus.push(unsafe { *ptr.add(2).cast::() }); + } + } + 1 => unsafe { + io_apic = Some(IOAPIC { + ioapic_id: *ptr.add(2), + _reserved: *ptr.add(3), + ptr: (*ptr.add(4).cast::()) as *mut u8, + global_interrupt_base: *ptr.add(8).cast::(), + }) + }, + 2 => unsafe { + io_apic_source_override = Some(IOAPICSourceOverride { + bus_source: *ptr.add(2), + irq_source: *ptr.add(3), + global_system_interrupt: *ptr.add(4).cast::(), + flags: *ptr.add(8).cast::(), + }) + }, + 5 => lapic_ptr = unsafe { *(ptr.add(4).cast::()) } as *mut u8, + _ => {} + } + + ptr = unsafe { ptr.add((*ptr.add(1)) as usize) }; + } + + if io_apic.is_none() || io_apic_source_override.is_none() { + return Err(()); + } + + let io_apic_ptr = io_apic.unwrap().ptr; + + crate::println!( + "Found {} cores, IOAPIC {:p}, LAPIC {lapic_ptr:p}, Processor IDs:", + cpus.len(), + io_apic_ptr, + ); + + for apic in &cpus { + crate::println!(" {}", apic.acpi_processor_id); + } + + let apic = Self { + io_apic: io_apic.unwrap(), + local_apic: lapic_ptr, + cpus: cpus.into(), + }; + + apic.write_lapic(0xF0, apic.read_lapic(0xF0) | 0x100); + + let io_apic_ver = apic.read_ioapic(0x01); + + let number_of_inputs = ((io_apic_ver >> 16) & 0xFF) + 1; + + crate::println!("{number_of_inputs}"); + + // Take the keyboard vector table, then mask out the top most bit (interrupt mask), then, + // mask out the bottom 8 bits, and put the kbd int in it setting the interrupt vector + let keyboard_vt = ((apic.read_ioapic(0x12) & 0x7FFF) & 0xFF00) | 0x21; + + // enable keyboard interrupt + apic.write_ioapic(0x12, keyboard_vt); + + return Ok(apic); + } + + pub fn read_ioapic(&self, reg: u32) -> u32 { + unsafe { + core::ptr::write_volatile(self.io_apic.ptr.cast::(), reg & 0xff); + return core::ptr::read_volatile(self.io_apic.ptr.cast::().add(4)); + } + } + + pub fn write_ioapic(&self, reg: u32, value: u32) { + unsafe { + core::ptr::write_volatile(self.io_apic.ptr.cast::(), reg & 0xff); + core::ptr::write_volatile(self.io_apic.ptr.cast::().add(4), value); + } + } + + pub fn read_lapic(&self, reg: u32) -> u32 { + unsafe { + return *self.local_apic.add(reg as usize).cast::(); + } + } + + pub fn write_lapic(&self, reg: u32, value: u32) { + unsafe { + *self.local_apic.add(reg as usize).cast::() = value; + } + } + + pub fn end_of_interrupt(&self) { + self.write_lapic(0xB0, 0x00); + } +} + +pub static APIC: Mutex> = Mutex::new(None); diff --git a/src/arch/x86_common/mod.rs b/src/arch/x86_common/mod.rs index f4e351c..654122e 100644 --- a/src/arch/x86_common/mod.rs +++ b/src/arch/x86_common/mod.rs @@ -1,3 +1,4 @@ +pub mod apic; pub mod io; pub mod pic; pub mod stack_trace; @@ -9,3 +10,27 @@ pub fn pause() { core::arch::asm!("pause"); }; } + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[inline(always)] +pub fn cpu_has_msr() -> bool { + return unsafe { core::arch::x86_64::__cpuid_count(1, 0).edx } & 1 << 5 != 0; +} + +pub unsafe fn cpu_get_msr(msr: u32, lo: &mut u32, hi: &mut u32) { + core::arch::asm!( + "rdmsr", + in("ecx") msr, + inout("eax") *lo, + inout("edx") *hi, + ); +} + +pub unsafe fn cpu_set_msr(msr: u32, lo: &u32, hi: &u32) { + core::arch::asm!( + "wrmsr", + in("ecx") msr, + in("eax") *lo, + in("edx") *hi, + ); +} diff --git a/src/arch/x86_common/stack_trace.rs b/src/arch/x86_common/stack_trace.rs index 180ce4f..757c77e 100644 --- a/src/arch/x86_common/stack_trace.rs +++ b/src/arch/x86_common/stack_trace.rs @@ -1,6 +1,6 @@ -use alloc::{borrow::ToOwned, format, string::String, vec::Vec}; +use alloc::{borrow::ToOwned, string::String, vec::Vec}; -use crate::drivers::{fs::vfs::VfsFileSystem, serial::write_serial}; +use crate::drivers::fs::vfs::VfsFileSystem; #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -20,6 +20,13 @@ pub fn print_stack_trace(max_frames: usize, rbp: u64) { let instruction_pointer = unsafe { (*stackframe).rip }; + if instruction_pointer == 0x0 { + unsafe { + stackframe = (*stackframe).back; + }; + continue; + } + crate::print!(" {:#X} ", instruction_pointer); let instrcution_info = get_function_name(instruction_pointer); diff --git a/src/drivers/acpi.rs b/src/drivers/acpi.rs index 4025b0d..39138d4 100644 --- a/src/drivers/acpi.rs +++ b/src/drivers/acpi.rs @@ -1,16 +1,13 @@ -// TODO: reduce the need to derive(Clone, Copy) everything - -use alloc::{sync::Arc, vec::Vec}; +use alloc::vec::Vec; use crate::{ arch::io::{inw, outb}, - libs::{lazy::Lazy, mutex::Mutex, oncecell::OnceCell}, - log_info, + libs::oncecell::OnceCell, }; #[repr(C, packed)] #[derive(Clone, Copy, Debug)] -struct SDTHeader { +pub struct SDTHeader { pub signature: [u8; 4], pub length: u32, pub revision: u8, @@ -23,23 +20,40 @@ struct SDTHeader { } #[repr(C, packed)] -#[derive(Clone, Copy, Debug)] -pub struct SDT { - pub header: SDTHeader, - pub inner: T, +#[derive(Debug)] +pub struct SDT<'a, T> { + pub header: &'a SDTHeader, + pub inner: &'a T, + pub extra: Option<&'a [u8]>, } -impl SDT { - unsafe fn new(pointer: *mut u8) -> Self { - let header = *(pointer.cast::()); - let inner = *(pointer.add(core::mem::size_of::()).cast::()); +impl<'a, T> SDT<'a, T> { + unsafe fn new(ptr: *const u8) -> Self { + let length = core::ptr::read_unaligned(ptr.add(4).cast::()); + let data = core::slice::from_raw_parts(ptr, length as usize); - return Self { header, inner }; + crate::log_serial!("SDT at: {ptr:p}"); + + assert!(data.len() == length as usize); + + let header: &SDTHeader = core::mem::transmute(data[0..].as_ptr()); + let inner: &T = core::mem::transmute(data[core::mem::size_of::()..].as_ptr()); + let mut extra = None; + + if length as usize > core::mem::size_of::() + core::mem::size_of::() { + extra = Some(&data[core::mem::size_of::() + core::mem::size_of::()..]); + } + + return Self { + header, + inner, + extra, + }; } } #[repr(C, packed)] -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] struct RSDP { signature: [u8; 8], checksum: u8, @@ -49,6 +63,7 @@ struct RSDP { } #[repr(C, packed)] +#[derive(Debug)] struct XSDP { rsdp: RSDP, @@ -70,17 +85,17 @@ struct XSDT { pointers: u64, } -#[derive(Clone, Copy, Debug)] -enum RootSDT { - RSDT(SDT), - XSDT(SDT), +#[derive(Debug)] +enum RootSDT<'a> { + RSDT(SDT<'a, RSDT>), + XSDT(SDT<'a, XSDT>), } -impl RootSDT { +impl<'a> RootSDT<'a> { fn header(&self) -> SDTHeader { return match self { - &RootSDT::RSDT(RSDT) => RSDT.header, - &RootSDT::XSDT(XSDT) => XSDT.header, + RootSDT::RSDT(RSDT) => *RSDT.header, + RootSDT::XSDT(XSDT) => *XSDT.header, }; } @@ -97,12 +112,12 @@ impl RootSDT { let mut offset = 0; let root_ptr = match self { - &RootSDT::RSDT(RSDT) => { + RootSDT::RSDT(RSDT) => { let ptrs = RSDT.inner.pointers as *const u8; assert!(!ptrs.is_null()); ptrs.add(offset) } - &RootSDT::XSDT(XSDT) => { + RootSDT::XSDT(XSDT) => { let ptrs = XSDT.inner.pointers as *const u8; assert!(!ptrs.is_null()); ptrs.add(offset) @@ -110,19 +125,17 @@ impl RootSDT { }; for _ in 0..idx { - let header = *root_ptr.add(offset).cast::(); + let header: &SDTHeader = core::mem::transmute(root_ptr.add(offset).cast::()); offset += header.length as usize; } - crate::println!("{offset:X?} {idx}"); - return root_ptr.add(offset); } } -#[derive(Clone, Debug)] -struct ACPI { - root_sdt: RootSDT, +#[derive(Debug)] +struct ACPI<'a> { + root_sdt: RootSDT<'a>, tables: Vec<[u8; 4]>, } @@ -138,8 +151,6 @@ fn resolve_acpi() { let rsdp = unsafe { &*rsdp_ptr.unwrap().address.as_ptr().unwrap().cast::() }; - log_info!("RSDP: {rsdp:X?}"); - // TODO: validate RSDT let root_sdt = { if rsdp.revision == 0 { @@ -150,8 +161,6 @@ fn resolve_acpi() { } }; - log_info!("{root_sdt:X?}"); - let tables: Vec<[u8; 4]> = (0..root_sdt.len()) .map(|i| { let sdt_ptr = unsafe { root_sdt.get(i) }; @@ -243,19 +252,35 @@ struct FADT { pub fn init_acpi() { resolve_acpi(); + crate::log_ok!("Found {} ACPI Tables!", ACPI.tables.len()); + + crate::log_serial!("Available serial tables:"); + for i in 0..ACPI.tables.len() { + crate::log_serial!(" {}", core::str::from_utf8(&ACPI.tables[i]).unwrap()) + } + let fadt = find_table::("FACP").expect("Failed to find FADT"); - crate::println!("{fadt:X?}"); + outb(fadt.inner.smi_cmd_port as u16, fadt.inner.acpi_enable); + + while inw(fadt.inner.pm1a_control_block as u16) & 1 == 0 {} + + crate::arch::interrupts::PICS.lock().write().disable(); + + *crate::arch::apic::APIC.lock().write() = + Some(crate::arch::apic::APIC::new().expect("Failed to enable APIC!")); + + crate::log_ok!("APIC enabled!"); } -pub fn find_table(table_name: &str) -> Option> { +pub fn find_table(table_name: &str) -> Option> { assert_eq!(table_name.len(), 4); for (i, table) in ACPI.tables.iter().enumerate() { if table == table_name.as_bytes() { - let ptr = unsafe { ACPI.root_sdt.get(i).cast::>() }; - crate::println!("Found {table_name} at index {i} {ptr:p}"); - let table = unsafe { *ptr }; + let ptr = unsafe { ACPI.root_sdt.get(i) }; + + let table = unsafe { SDT::new(ptr) }; return Some(table); } } diff --git a/src/drivers/keyboard.rs b/src/drivers/keyboard.rs index 19fe023..fd79b72 100755 --- a/src/drivers/keyboard.rs +++ b/src/drivers/keyboard.rs @@ -21,10 +21,7 @@ static EXTENDED_KEY: AtomicBool = AtomicBool::new(false); #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub extern "x86-interrupt" fn keyboard_interrupt_handler() { - interrupts::PICS - .lock() - .write() - .notify_end_of_interrupt(interrupts::InterruptIndex::Keyboard.as_u8()); + interrupts::signal_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); let scancode = inb(KBD_DATA_PORT); diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index 3286f8f..0a043b1 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -1,3 +1,4 @@ +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub mod acpi; pub mod fs; pub mod keyboard; diff --git a/src/drivers/serial.rs b/src/drivers/serial.rs index ef07464..ebb88e6 100644 --- a/src/drivers/serial.rs +++ b/src/drivers/serial.rs @@ -42,6 +42,14 @@ pub fn init_serial() -> u8 { return 0; } +pub fn write_string(string: &str) { + for &ch in string.as_bytes() { + write_serial(ch as char); + } + write_serial('\n'); + write_serial('\r'); +} + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] pub fn init_serial() -> u8 { return 0; diff --git a/src/drivers/storage/ide.rs b/src/drivers/storage/ide.rs index 499df8d..859613e 100755 --- a/src/drivers/storage/ide.rs +++ b/src/drivers/storage/ide.rs @@ -241,6 +241,9 @@ impl ATABus { outb(self.io_bar + ATADriveDataRegister::LBA1 as u16, 0); outb(self.io_bar + ATADriveDataRegister::LBA2 as u16, 0); + // disable interrupts by setting bit 2 in the control port + outb(self.control_bar + 0x0C - 0x0A, 2); + self.send_command(ATADriveCommand::Identify); if self.status() == 0x00 { diff --git a/src/libs/logging.rs b/src/libs/logging.rs index dcefb55..4fa9d26 100644 --- a/src/libs/logging.rs +++ b/src/libs/logging.rs @@ -3,6 +3,13 @@ macro_rules! log_info { ($($arg:tt)*) => ($crate::println!("\033[97m[ \033[90m? \033[97m]\033[0m {}", &alloc::format!($($arg)*))); } +#[macro_export] +macro_rules! log_serial { + ($($arg:tt)*) => ( + $crate::drivers::serial::write_string(&alloc::format!($($arg)*)) + ); +} + #[macro_export] macro_rules! log_error { ($($arg:tt)*) => ($crate::println!("\033[97m[ \033[91m! \033[97m]\033[0m {}", &alloc::format!($($arg)*))); diff --git a/src/main.rs b/src/main.rs index 085d274..b5f07b4 100755 --- a/src/main.rs +++ b/src/main.rs @@ -28,10 +28,11 @@ pub extern "C" fn _start() -> ! { serial::init_serial(); - mem::log_info(); - + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] drivers::acpi::init_acpi(); + mem::log_info(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] drivers::pci::enumerate_pci_bus(); diff --git a/src/mem/mod.rs b/src/mem/mod.rs index cb0c5a3..b8e17b9 100755 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -137,17 +137,13 @@ pub fn log_memory_map() { let memmap = memmap_request.unwrap().memmap(); - crate::println!("====== MEMORY MAP ======"); + crate::log_serial!("====== MEMORY MAP ======"); for entry in memmap.iter() { let label = (entry.len as usize).label_bytes(); - crate::println!( - "[ {:#018X?} ] Type: \033[{};m{:?}\033[0;m Size: {}", + crate::log_serial!( + "[ {:#018X?} ] Type: {:?} Size: {}", entry.base..entry.base + entry.len, - match entry.typ { - limine::MemoryMapEntryType::Usable => 32, - _ => 31, - }, entry.typ, label )