diff --git a/README.md b/README.md index fe03e6b..0b89a8a 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,8 @@ Alternatively, you may disable using a UEFI firmware with qemu like so: USE_LEGACY_BIOS=1 cargo run ``` +## Debugging + +See [debugging](docs/Debugging/DEBUGGING.md) for some help with this, including commands to help with disassembly. + If you have any other issues, feel free to create an issue or a PR. \ No newline at end of file diff --git a/debug.txt b/debug.txt new file mode 100644 index 0000000..a69750b --- /dev/null +++ b/debug.txt @@ -0,0 +1,17 @@ + Running kernel in debug mode. +info: Creating build directory structure +info: Copying files to ISO root + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/build/target/x86_64-kernel/debug/kernel to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/boot/kernel + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/config/limine.conf to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/boot/limine/limine.conf + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/build/limine/limine-bios-cd.bin to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/boot/limine/ + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/build/limine/limine-uefi-cd.bin to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/boot/limine/ + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/build/limine/limine-bios.sys to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/boot/limine/ + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/build/limine/BOOTX64.EFI to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/EFI/BOOT/ + Copying: /home/fantasypvp/Projects/OSdev/FoundryOS/build/limine/BOOTIA32.EFI to /home/fantasypvp/Projects/OSdev/FoundryOS/build/iso_root/EFI/BOOT/ +Building: bootable ISO image +info: Installing Limine bootloader +info: KVM acceleration enabled +info: Running OS in QEMU... +[=3h[=3h[=3hBdsDxe: loading Boot0001 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0) +BdsDxe: starting Boot0001 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0) +map worked!got apic base \ No newline at end of file diff --git a/docs/Debugging/DEBUGGING.md b/docs/Debugging/DEBUGGING.md new file mode 100644 index 0000000..06cd260 --- /dev/null +++ b/docs/Debugging/DEBUGGING.md @@ -0,0 +1,16 @@ +# Debugging the Kernel + +Here we will add some helpful tips on debugging the kernel. + +## Disassembling a public function + +To disassemble a public function, first we need a symbol in the public symbol table, so start by making the function fully public (including any parent modules). Do this as though you are trying to make a public function for a library crate (this includes the `kernel` crate). Simply mark the function and any parent modules as public, up until the point of [lib.rs (kernel link)](../../kernel/src/lib.rs). + +Then, we need to find the specific demangled symbol to disassemble, because the default objdump output can be very verbose. + +```sh +# Change as required, I pipe to less and /SEARCH FOR FUNCTION HERE. +nm --demangle ./build/target/x86_64-kernel/debug/kernel | less +# Now just paste the symbol where it says YOUR_SYMBOL_HERE and profit. Use -Mintel for Intel assembly syntax. +objdump -Matt --source --line-numbers --visualize-jumps ./build/target/x86_64-kernel/debug/kernel --demangle=rust --disassemble="YOUR_SYMBOL_HERE" +``` diff --git a/kernel/src/arch/x86_64/apic/mod.rs b/kernel/src/arch/x86_64/apic/mod.rs new file mode 100644 index 0000000..9199a09 --- /dev/null +++ b/kernel/src/arch/x86_64/apic/mod.rs @@ -0,0 +1,115 @@ +use core::arch::x86_64::__cpuid; + +use libk::drivers::memory::FoundryOSFrameAllocator; +use spin::Lazy; +use x86_64::{ + PhysAddr, VirtAddr, + instructions::port::Port, + registers::model_specific::Msr, + structures::paging::{ + FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, Translate, + }, +}; + +use crate::serial_print; + +use super::{cpu::model_specific_registers::*, memmap::PHYSICAL_MEMORY_OFFSET}; + +const IA32_APIC_BASE_MSR: u32 = 0x1b; +const IA32_APIC_BASE_MSR_BSP: u64 = 0x100; +const IA32_APIC_BASE_MSR_ENABLE: u64 = 0x800; +const IA32_APIC_BASE_MSR_DISABLE: u64 = !IA32_APIC_BASE_MSR_ENABLE; + +const CPUID_FEAT_EDX_APIC: u64 = 1 << 9; // the cpuid instruction will return this flag if it supports APIC + +// const APIC_VIRTUAL_ADDRESS: Lazy = Lazy::new(|| { +// let apic_base = get_apic_base(); +// let virt_addr = unsafe { phys_to_virt(apic_base) }; + +// virt_addr +// }); + +fn set_apic_base_enable(apic: PhysAddr) { + let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE; + cpu_set_msr(IA32_APIC_BASE_MSR, rax); +} + +fn set_apic_base_disable(apic: PhysAddr) { + let rax = (apic.as_u64() & 0xfffff0000) & IA32_APIC_BASE_MSR_DISABLE; + cpu_set_msr(IA32_APIC_BASE_MSR, rax); +} + +fn get_apic_base() -> PhysAddr { + let mut value: u64 = 0; + cpu_get_msr(IA32_APIC_BASE_MSR, &mut value); + PhysAddr::new(value & 0xfffff0000) +} + +fn write_apic_register(apic_base: &VirtAddr, reg: u8, value: u32) { + let apic_base = apic_base.as_u64(); + let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64; + unsafe { *(reg_addr as *mut u32) = value }; +} + +fn read_apic_register(apic_base: &VirtAddr, reg: u8) -> u32 { + let apic_base = apic_base.as_u64(); + + serial_print!("got apic base"); + + let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64; + unsafe { *(reg_addr as *const u32) } +} + +pub fn check_apic() -> bool { + let res = unsafe { __cpuid(1) }; + res.edx as u64 & CPUID_FEAT_EDX_APIC != 0 +} + +#[inline(always)] +unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr { + let phys = phys.as_u64(); + match phys.checked_add(*PHYSICAL_MEMORY_OFFSET) { + Some(virt) => { + serial_print!("map worked!"); + VirtAddr::new(virt) + } + None => { + serial_print!("THIS IS A PROBLEM"); + panic!("overflow") + } + } +} + +pub fn enable_apic( + mapper: &mut impl Mapper, + frame_allocator: &mut FoundryOSFrameAllocator, +) { + let apic_phys_addr = get_apic_base(); + set_apic_base_enable(apic_phys_addr); + // map virt address of apic + + let apic_virt = unsafe { phys_to_virt(apic_phys_addr) }; + // let page: Page = Page::containing_address(apic_virt); + // let frame: PhysFrame = PhysFrame::containing_address(apic_phys_addr); + // let flags: PageTableFlags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + + // unsafe { + // match mapper.map_to(page, frame, flags, frame_allocator) { + // Ok(_) => {} + // Err(why) => panic!("failed to map apic: {:?}", why), + // } + // } + + // // FIXME: this causes a page fault + // // TODO: map to virtual memor + + let reg = read_apic_register(&apic_virt, 0xF0); + + serial_print!("ok2"); + + write_apic_register(&apic_virt, 0xF0, reg | 0x100); +} + +pub struct Apic {} + +pub enum ApicVector {} diff --git a/libk/src/drivers/cpu.rs b/kernel/src/arch/x86_64/cpu.rs similarity index 100% rename from libk/src/drivers/cpu.rs rename to kernel/src/arch/x86_64/cpu.rs diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 2d9dd92..7476eb6 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,11 +1,14 @@ -use libk::drivers::apic::enable_apic; +use libk::drivers::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; use libk::prelude::*; use pic8259::ChainedPics; use x86_64::registers::control::Cr2; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; +use x86_64::structures::paging::mapper::MapperFlushAll; +use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}; use spin::{Lazy, Mutex}; +use super::apic::enable_apic; use super::gdt; static IDT: Lazy = Lazy::new(|| { @@ -52,14 +55,21 @@ impl InterruptIndex { pub fn init_idt() { IDT.load(); - // enable_apic(); - // TODO: fix apic +} + +pub fn enable_pic() { unsafe { PICS.lock().initialize(); PICS.lock().write_masks(0xfc, 0xff); } } +pub fn disable_pic() { + unsafe { + PICS.lock().disable(); + } +} + extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { serial_println!("Exception: Breakpoint\n{:#?}", stack_frame); println_log!("Exception: Breakpoint\n{:#?}", stack_frame); @@ -118,10 +128,28 @@ extern "x86-interrupt" fn page_fault_handler( stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode, ) { - serial_println!("Exception: Page Fault"); - serial_println!("Accessed Address: {:?}", Cr2::read()); - serial_println!("Error Code: {:?}", error_code); - serial_println!("{:#?}", stack_frame); + // serial_println!("Exception: Page Fault"); + // serial_println!("Accessed Address: {:?}", Cr2::read()); + // serial_println!("Error Code: {:?}", error_code); + // serial_println!("{:#?}", stack_frame); - crate::hcf(); + if let Some(frame_allocator) = FRAME_ALLOCATOR.get() { + let mut f = frame_allocator.lock(); + + let frame = f.allocate_frame().unwrap(); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + let page: Page = Page::containing_address(Cr2::read().unwrap()); + + unsafe { + let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); + + match mapper.map_to(page, frame, flags, &mut *f) { + Ok(_) => {} + Err(why) => panic!("failed to map page: {:?}", why), + } + } + MapperFlushAll::new().flush_all(); + } else { + panic!("failed to get frame allocator"); + } } diff --git a/kernel/src/arch/x86_64/memmap.rs b/kernel/src/arch/x86_64/memmap.rs index 17362e1..1df8437 100644 --- a/kernel/src/arch/x86_64/memmap.rs +++ b/kernel/src/arch/x86_64/memmap.rs @@ -14,6 +14,10 @@ static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new(); #[unsafe(link_section = ".requests")] static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new(); +#[used] +#[unsafe(link_section = ".requests")] +static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new(); + /// ```rs /// let virt_addr = phys_addr + offset; /// let phys_addr = virt_addr - offset; // (given VA is in the HHDM). Do not use for executable code. @@ -25,10 +29,6 @@ pub static PHYSICAL_MEMORY_OFFSET: Lazy = Lazy::new(|| { .offset() }); -#[used] -#[unsafe(link_section = ".requests")] -static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new(); - /// Converts virtual addresses in the kernel to a physical address like this: /// ```rs /// let phys_addr = virt_addr - virtual_base + physical_base; diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 7947954..7ffe935 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -2,6 +2,8 @@ pub mod gdt; pub mod interrupts; -pub mod memory; - pub mod memmap; + +pub mod apic; + +pub mod cpu; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 7e60267..48d4c75 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -13,7 +13,9 @@ extern crate alloc; +use arch::x86_64::apic::enable_apic; use core::arch::asm; +use libk::drivers::memory; use limine::BaseRevision; use libk::drivers::kalloc::allocator::init_heap; @@ -56,7 +58,7 @@ pub fn boot() -> Result<(), &'static str> { return Err("base revision not supported"); } - use arch::x86_64::{gdt, interrupts, memmap, memory}; + use arch::x86_64::{gdt, interrupts, memmap}; let memory_map = memmap::get_memory_map(); @@ -75,21 +77,33 @@ pub fn boot() -> Result<(), &'static str> { interrupts::init_idt(); println_log!("[Success]"); - print_log!(" Setting Up Page Table... "); - let mut frame_allocator = unsafe { memory::FoundryOSFrameAllocator::init(memory_map) }; - println_log!("[Success]"); - print_log!(" Initialising Memory Subsystem... "); let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET); - let mut l4_table = unsafe { memory::init(physical_memory_offset) }; + let mut l4_table = memory::init_page_table(physical_memory_offset); + println_log!("[Success]"); + + print_log!(" Setting Up Page Table... "); + memory::init_frame_allocator(memory_map); println_log!("[Success]"); print_log!(" Initialising Heap... "); - if init_heap(&mut l4_table, &mut frame_allocator).is_err() { + if init_heap().is_err() { return Err("Failed to initialise heap: error"); } println_log!("[Success]"); + print_log!(" Enabling PICs... "); + interrupts::enable_pic(); + println_log!("[Success]"); + + // print_log!(" Disabling PICs... "); + // interrupts::disable_pic(); + // println_log!("[Success]"); + // + // print_log!(" Initialising APIC"); + // enable_apic(&mut l4_table, &mut frame_allocator); + // println_log!("[Success]"); + print_log!(" Enabling Interrupts... "); x86_64::instructions::interrupts::enable(); println_log!("[Success]"); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 22624c1..7348d45 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -38,6 +38,13 @@ extern "C" fn kmain() -> ! { println!("vec 100000 works"); let mut executor = Executor::new(); + + let mut v = Vec::with_capacity(1000 * 1000); + for i in 0..1000 * 1000 { + v.push(i); + } + println!("v.len(): {}", v.len()); + executor.spawn(Task::new(shell())); executor.run(); } diff --git a/libk/src/drivers/apic/mod.rs b/libk/src/drivers/apic/mod.rs deleted file mode 100644 index 7e47e8f..0000000 --- a/libk/src/drivers/apic/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -use core::arch::x86_64::__cpuid; - -use x86_64::{PhysAddr, instructions::port::Port, registers::model_specific::Msr}; - -use super::cpu::model_specific_registers::*; - -const IA32_APIC_BASE_MSR: u32 = 0x1b; -const IA32_APIC_BASE_MSR_BSP: u64 = 0x100; -const IA32_APIC_BASE_MSR_ENABLE: u64 = 0x800; - -const CPUID_FEAT_EDX_APIC: u64 = 1 << 9; // the cpuid instruction will return this flag if it supports APIC - -fn set_apic_base(apic: PhysAddr) { - let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE; - cpu_set_msr(IA32_APIC_BASE_MSR, rax); -} - -fn get_apic_base() -> PhysAddr { - let mut value: u64 = 0; - cpu_get_msr(IA32_APIC_BASE_MSR, &mut value); - PhysAddr::new(value & 0xfffff0000) -} - -fn write_apic_register(reg: u8, value: u32) { - let apic_base = get_apic_base().as_u64(); - let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64; - unsafe { *(reg_addr as *mut u32) = value }; -} - -fn read_apic_register(reg: u8) -> u32 { - let apic_base = get_apic_base().as_u64(); - let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64; - unsafe { *(reg_addr as *const u32) } -} - -pub fn check_apic() -> bool { - let res = unsafe { __cpuid(1) }; - res.edx as u64 & CPUID_FEAT_EDX_APIC != 0 -} - -pub fn enable_apic() { - set_apic_base(get_apic_base()); - - write_apic_register(0xF0, read_apic_register(0xF0) | 0x100); -} - -pub struct Apic {} - -pub enum ApicVector {} diff --git a/libk/src/drivers/kalloc/allocator.rs b/libk/src/drivers/kalloc/allocator.rs index 6e87949..5f8949b 100644 --- a/libk/src/drivers/kalloc/allocator.rs +++ b/libk/src/drivers/kalloc/allocator.rs @@ -2,10 +2,16 @@ use linked_list_allocator::LockedHeap; use x86_64::{ VirtAddr, structures::paging::{ - FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, mapper::MapToError, + FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, + mapper::{MapToError, MapperFlushAll}, }, }; +use crate::{ + drivers::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}, + serial_print, serial_println, +}; + /// We are currently using a linked list heap allocator which uses our underlying page allocator. pub type FoundryAllocator = LockedHeap; @@ -14,28 +20,50 @@ pub type FoundryAllocator = LockedHeap; static ALLOCATOR: FoundryAllocator = FoundryAllocator::empty(); pub const HEAP_START: usize = 0x4444_4444_0000; -pub const HEAP_SIZE: usize = 1000 * 1024; +pub const HEAP_SIZE: usize = 1024 * 1024 * 1024; /// Sets up the heap using the backing page frame allocator. -pub fn init_heap( - mapper: &mut impl Mapper, - frame_allocator: &mut impl FrameAllocator, -) -> Result<(), MapToError> { - let range = { - let heap_start = VirtAddr::new(HEAP_START as u64); - let heap_end = heap_start + HEAP_SIZE as u64 - 1u64; - let heap_start_page = Page::::containing_address(heap_start); - let heap_end_page = Page::::containing_address(heap_end); - Page::range_inclusive(heap_start_page, heap_end_page) - }; +pub fn init_heap() -> Result<(), MapToError> { + // let mut frame_allocator = if let Some(f) = FRAME_ALLOCATOR.get() { + // f.lock() + // } else { + // return Err(MapToError::FrameAllocationFailed); + // }; - for page in range { - let frame = frame_allocator - .allocate_frame() - .ok_or(MapToError::FrameAllocationFailed)?; - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; - } + // let mut mapper = if let Some(m) = OFFSET_PAGE_TABLE.get() { + // m.lock() + // } else { + // return Err(MapToError::FrameAllocationFailed); + // }; + + // let range = { + // let heap_start = VirtAddr::new(HEAP_START as u64); + // let heap_end = heap_start + HEAP_SIZE as u64 - 1u64; + // let heap_start_page = Page::::containing_address(heap_start); + // let heap_end_page = Page::::containing_address(heap_end); + // Page::range_inclusive(heap_start_page, heap_end_page) + // }; + + // let usable_frames = frame_allocator.count_usable_frames(); + // serial_println!("usable frames: {}", usable_frames); + + // let mut i = 0; + // for page in range { + // i += 1; + // if i % 128 == 0 { + // serial_println!("allocated {} pages", i); + // } + // let frame = frame_allocator + // .allocate_frame() + // .ok_or(MapToError::FrameAllocationFailed)?; + // let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + // unsafe { + // // IMPORTANT: make sure to flush the mapper!!!! + // let _ = mapper.map_to(page, frame, flags, &mut *frame_allocator)?; + // } + // } + + // MapperFlushAll::new().flush_all(); unsafe { ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE); diff --git a/kernel/src/arch/x86_64/memory.rs b/libk/src/drivers/memory.rs similarity index 85% rename from kernel/src/arch/x86_64/memory.rs rename to libk/src/drivers/memory.rs index 09cc345..2e53a01 100644 --- a/kernel/src/arch/x86_64/memory.rs +++ b/libk/src/drivers/memory.rs @@ -1,20 +1,17 @@ // use lib_alloc::allocator::FoundryAllocator; use limine::{memory_map::EntryType, response::MemoryMapResponse}; +use spin::{Mutex, Once}; use x86_64::{ - PhysAddr, - VirtAddr, - // addr, + PhysAddr, VirtAddr, registers::control::Cr3, structures::paging::{ - // page_table::FrameError, - FrameAllocator, - OffsetPageTable, - PageTable, - PhysFrame, - Size4KiB, + FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB, + page_table::FrameError, }, }; +pub static FRAME_ALLOCATOR: Once> = Once::new(); +pub static OFFSET_PAGE_TABLE: Once> = Once::new(); /// Returns a mutable reference to the current level 4 page table. /// /// # Safety @@ -48,10 +45,11 @@ unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut Page /// /// Returns an `OffsetPageTable` that allows for manipulation of the page /// tables for the current CPU architecture. -pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { +pub fn init_page_table(physical_memory_offset: VirtAddr) { unsafe { let l4_table = active_l4_table(physical_memory_offset); - OffsetPageTable::new(l4_table, physical_memory_offset) + let offset_table = OffsetPageTable::new(l4_table, physical_memory_offset); + OFFSET_PAGE_TABLE.call_once(|| Mutex::new(offset_table)); } } @@ -60,6 +58,12 @@ pub struct FoundryOSFrameAllocator { next: usize, } +pub fn init_frame_allocator(memory_map: &'static MemoryMapResponse) { + unsafe { + FRAME_ALLOCATOR.call_once(|| Mutex::new(FoundryOSFrameAllocator::init(memory_map))); + } +} + impl FoundryOSFrameAllocator { /// Creates a new `FoundryOSFrameAllocator` from a memory map. /// @@ -73,6 +77,10 @@ impl FoundryOSFrameAllocator { } } + pub fn count_usable_frames(&self) -> u32 { + self.usable_frames().count() as u32 + } + /// An iterator over all usable frames in the memory map. /// /// Yields one `PhysFrame` for each available 4KiB frame in the memory map. diff --git a/libk/src/drivers/mod.rs b/libk/src/drivers/mod.rs index 0222bb5..c3e42d1 100644 --- a/libk/src/drivers/mod.rs +++ b/libk/src/drivers/mod.rs @@ -1,7 +1,6 @@ pub mod io; pub mod kalloc; -pub mod apic; pub mod async_io; -pub mod cpu; +pub mod memory; pub mod pic; diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..8c7e578 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# Colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# Error handling +set -e +trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR + +# Get absolute path to project root +project_root=$(cd "$script_dir/.." &>/dev/null && pwd) +build_dir="$project_root/build" +iso_root="$build_dir/iso_root" + +# Logging functions +info() { + echo -e "${BLUE}${BOLD}info${NC}: $1" +} + +compiling() { + echo -e "${GREEN}${BOLD}Compiling${NC}: $1" +} + +warning() { + echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2 +} + +building() { + echo -e "${GREEN}${BOLD}Building${NC}: $1" +} + +copying() { + echo -e "${GREEN}${BOLD} Copying${NC}: $1 to $2" +} + +error() { + echo -e "${RED}${BOLD}error${NC}: $1" >&2 + exit 1 +} + +copy_file() { + copying $1 $2 + cp "$1" "$2" || error $3 +} + +# Check if we're running tests +if [[ $1 == *"deps"* ]]; then + kernel_path="$1" +else + # Build the kernel normally + cd "$project_root" + # cargo build + kernel_path="$build_dir/target/x86_64-kernel/debug/kernel" +fi + +# Check for required tools +check_tools() { + local missing=0 + for tool in xorriso git qemu-system-x86_64; do + if ! command -v $tool >/dev/null 2>&1; then + error "required tool '$tool' is not installed" + missing=1 + fi + done + if [ $missing -eq 1 ]; then + error "missing required tools" + fi +} + +# Create build directory structure +info "Creating build directory structure" +mkdir -p "$iso_root/boot/limine" +mkdir -p "$iso_root/EFI/BOOT" + +# Clone Limine if needed +if [ ! -d "$build_dir/limine" ]; then + compiling "limine bootloader" + cd "$build_dir" + git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine" + make -C "$build_dir/limine" || error "failed to build limine" + cd "$project_root" +fi + +# Copy files +info "Copying files to ISO root" +copy_file "$kernel_path" "$iso_root/boot/kernel" "failed to copy kernel" +copy_file "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" "failed to copy limine config" +copy_file "$build_dir/limine/limine-bios-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-bios-cd.bin" +copy_file "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-uefi-cd.bin" +copy_file "$build_dir/limine/limine-bios.sys" "$iso_root/boot/limine/" "failed to copy limine-bios.sys" +copy_file "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTX64.EFI" +copy_file "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTIA32.EFI" + +# Create ISO +building "bootable ISO image" +xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \ + -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \ + -apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \ + -efi-boot-part --efi-boot-image --protective-msdos-label \ + "$iso_root" -o "$build_dir/image.iso" || error "failed to create ISO" + +# Install Limine +info "Installing Limine bootloader" +"$build_dir/limine/limine" bios-install "$build_dir/image.iso" || error "failed to install limine" \ No newline at end of file diff --git a/scripts/flash_iso.sh b/scripts/flash_iso.sh new file mode 100755 index 0000000..3e98e7a --- /dev/null +++ b/scripts/flash_iso.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +source scripts/build.sh + +warning "This script will OVERWRITE whatever media you throw at it\nwith the built ISO." + +info "sudo ./hardware.sh /dev/yourdisk" + +if ! echo "$1" | grep -q "/dev"; then + error "invalid device" +fi + +read -p "Confirm (y/n): " confirmation +if [ "$confirmation" != "y" ]; then + exit 0 +fi + +dd if=./build/image.iso of="$1" diff --git a/scripts/hardware.sh b/scripts/hardware.sh deleted file mode 100755 index cd03a7c..0000000 --- a/scripts/hardware.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Colors -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[0;33m' -RED='\033[0;31m' -BOLD='\033[1m' -NC='\033[0m' # No Color - -info() { - echo -e "${BLUE}${BOLD}info${NC}: $1" -} - -warning() { - echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2 -} - -warning "This script will OVERWRITE whatever media you throw at it\nwith the built ISO." -info "sudo ./hardware.sh /dev/yourdisk" - -if echo "$1" | grep -q "/dev"; then - dd if=./build/image.iso of="$1" -fi diff --git a/scripts/run_debug.sh b/scripts/run_debug.sh index 91bcdb6..cf58cdb 100755 --- a/scripts/run_debug.sh +++ b/scripts/run_debug.sh @@ -2,120 +2,16 @@ # Script originally written by zxq5, I added separate scripts to remove `jq` dependency. -# Colors -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[0;33m' -RED='\033[0;31m' -BOLD='\033[1m' -NC='\033[0m' # No Color +script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +source $script_dir/build.sh # Error handling set -e trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR -# Get absolute path to project root -script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) -project_root=$(cd "$script_dir/.." &>/dev/null && pwd) - echo -e "${GREEN}${BOLD} Running kernel in debug mode." -# Logging functions -info() { - echo -e "${BLUE}${BOLD}info${NC}: $1" -} - -compiling() { - echo -e "${GREEN}${BOLD}Compiling${NC}: $1" -} - -warning() { - echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2 -} - -building() { - echo -e "${GREEN}${BOLD}Building${NC}: $1" -} - -copying() { - echo -e "${GREEN}${BOLD} Copying${NC}: $1 to $2" -} - -error() { - echo -e "${RED}${BOLD}error${NC}: $1" >&2 - exit 1 -} - -copy_file() { - copying $1 $2 - cp "$1" "$2" || error $3 -} - -build_dir="$project_root/build" -iso_root="$build_dir/iso_root" - -# Check if we're running tests -is_test=0 -if [[ $1 == *"deps"* ]]; then - is_test=1 - kernel_path="$1" -else - # Build the kernel normally - cd "$project_root" - # cargo build - kernel_path="$build_dir/target/x86_64-kernel/debug/kernel" -fi - -# Check for required tools -check_tools() { - local missing=0 - for tool in xorriso git qemu-system-x86_64; do - if ! command -v $tool >/dev/null 2>&1; then - error "required tool '$tool' is not installed" - missing=1 - fi - done - if [ $missing -eq 1 ]; then - error "missing required tools" - fi -} - -# Create build directory structure -info "Creating build directory structure" -mkdir -p "$iso_root/boot/limine" -mkdir -p "$iso_root/EFI/BOOT" - -# Clone Limine if needed -if [ ! -d "$build_dir/limine" ]; then - compiling "limine bootloader" - cd "$build_dir" - git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine" - make -C "$build_dir/limine" || error "failed to build limine" - cd "$project_root" -fi - -# Copy files -info "Copying files to ISO root" -copy_file "$kernel_path" "$iso_root/boot/kernel" "failed to copy kernel" -copy_file "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" "failed to copy limine config" -copy_file "$build_dir/limine/limine-bios-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-bios-cd.bin" -copy_file "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-uefi-cd.bin" -copy_file "$build_dir/limine/limine-bios.sys" "$iso_root/boot/limine/" "failed to copy limine-bios.sys" -copy_file "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTX64.EFI" -copy_file "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTIA32.EFI" - -# Create ISO -building "bootable ISO image" -xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \ - -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \ - -apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \ - -efi-boot-part --efi-boot-image --protective-msdos-label \ - "$iso_root" -o "$build_dir/image.iso" || error "failed to create ISO" - -# Install Limine -info "Installing Limine bootloader" -"$build_dir/limine/limine" bios-install "$build_dir/image.iso" || error "failed to install limine" - # Check if KVM is available if [ "${KVM_FLAG:-enable}" = "disable" ]; then warning "KVM acceleration disabled by user" @@ -135,6 +31,24 @@ else debug_flags="" fi +# Set up test-specific flags +if [ $is_test -eq 1 ]; then + test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none" + serial_flags="-serial stdio" +else + test_flags="" + # serial_flags="-serial tcp:127.0.0.1:1234,server -monitor telnet:127.0.0.1:1235,server" + serial_flags="-serial stdio" +fi + +# Set up VM memory +if [ $VM_MEMORY ]; then + vm_memory_flag="-m $VM_MEMORY" +else + vm_memory_flag="-m 2G" +fi + +# Set up boot flags if [ $USE_LEGACY_BIOS ]; then boot_flags="" else @@ -152,16 +66,6 @@ else -drive if=pflash,format=raw,file=$build_dir/RELEASEX64_OVMF_VARS.fd" fi -# Set up test-specific flags -if [ $is_test -eq 1 ]; then - test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none" - serial_flags="-serial stdio" -else - test_flags="" - # serial_flags="-serial tcp:127.0.0.1:1234,server -monitor telnet:127.0.0.1:1235,server" - serial_flags="-serial stdio" -fi - # Run in QEMU if [[ ${QEMU_FLAGS} == *-S* ]]; then info "Running OS in QEMU with GDB debugging enabled" @@ -188,20 +92,14 @@ check_test_res() { fi } - -kvm_flag="" - trap 'check_test_res "tests completed"' ERR - # $build_dir/image.iso - - cd "$project_root" qemu-system-x86_64 -M q35 \ -cdrom "$build_dir/image.iso" \ ${kvm_flag} \ -boot d \ - -m 2G \ + ${vm_memory_flag} \ ${serial_flags} \ -no-reboot \ ${test_flags} \ diff --git a/scripts/run_release.sh b/scripts/run_release.sh index c410362..b28cb0a 100755 --- a/scripts/run_release.sh +++ b/scripts/run_release.sh @@ -2,120 +2,23 @@ # Script originally written by zxq5, I added separate scripts to remove `jq` dependency. -# Colors -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[0;33m' -RED='\033[0;31m' -BOLD='\033[1m' -NC='\033[0m' # No Color +script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +source $script_dir/build.sh # Error handling set -e trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR -# Get absolute path to project root -script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) -project_root=$(cd "$script_dir/.." &>/dev/null && pwd) - echo -e "${GREEN}${BOLD} Running kernel in release mode." -# Logging functions -info() { - echo -e "${BLUE}${BOLD}info${NC}: $1" -} -compiling() { - echo -e "${GREEN}${BOLD}Compiling${NC}: $1" -} - -warning() { - echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2 -} - -building() { - echo -e "${GREEN}${BOLD}Building${NC}: $1" -} - -copying() { - echo -e "${GREEN}${BOLD} Copying${NC}: $1 to $2" -} - -error() { - echo -e "${RED}${BOLD}error${NC}: $1" >&2 - exit 1 -} - -copy_file() { - copying $1 $2 - cp "$1" "$2" || error $3 -} - -build_dir="$project_root/build" -iso_root="$build_dir/iso_root" - -# Check if we're running tests -is_test=0 -if [[ $1 == *"deps"* ]]; then - is_test=1 - kernel_path="$1" +if [ $VM_MEMORY ]; then + vm_memory_flag="-m $VM_MEMORY" else - # Build the kernel normally - cd "$project_root" - # cargo build - kernel_path="$build_dir/target/x86_64-kernel/release/kernel" + vm_memory_flag="-m 2G" fi -# Check for required tools -check_tools() { - local missing=0 - for tool in xorriso git qemu-system-x86_64; do - if ! command -v $tool >/dev/null 2>&1; then - error "required tool '$tool' is not installed" - missing=1 - fi - done - if [ $missing -eq 1 ]; then - error "missing required tools" - fi -} - -# Create build directory structure -info "Creating build directory structure" -mkdir -p "$iso_root/boot/limine" -mkdir -p "$iso_root/EFI/BOOT" - -# Clone Limine if needed -if [ ! -d "$build_dir/limine" ]; then - compiling "limine bootloader" - cd "$build_dir" - git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine" - make -C "$build_dir/limine" || error "failed to build limine" - cd "$project_root" -fi - -# Copy files -info "Copying files to ISO root" -copy_file "$kernel_path" "$iso_root/boot/kernel" "failed to copy kernel" -copy_file "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" "failed to copy limine config" -copy_file "$build_dir/limine/limine-bios-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-bios-cd.bin" -copy_file "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-uefi-cd.bin" -copy_file "$build_dir/limine/limine-bios.sys" "$iso_root/boot/limine/" "failed to copy limine-bios.sys" -copy_file "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTX64.EFI" -copy_file "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTIA32.EFI" - -# Create ISO -building "bootable ISO image" -xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \ - -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \ - -apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \ - -efi-boot-part --efi-boot-image --protective-msdos-label \ - "$iso_root" -o "$build_dir/image.iso" || error "failed to create ISO" - -# Install Limine -info "Installing Limine bootloader" -"$build_dir/limine/limine" bios-install "$build_dir/image.iso" || error "failed to install limine" - # Check if KVM is available if [ "${KVM_FLAG:-enable}" = "disable" ]; then warning "KVM acceleration disabled by user" @@ -128,9 +31,6 @@ else kvm_flag="" fi -# I'm lazy but I just remove GDB flags when running this script. -debug_flags="" - if [ $USE_LEGACY_BIOS ]; then boot_flags="" else @@ -148,56 +48,13 @@ else -drive if=pflash,format=raw,file=$build_dir/RELEASEX64_OVMF_VARS.fd" fi -# Set up test-specific flags -if [ $is_test -eq 1 ]; then - test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none" - serial_flags="-serial stdio" -else - test_flags="" - # serial_flags="-serial tcp:127.0.0.1:1234,server -monitor telnet:127.0.0.1:1235,server" - serial_flags="-serial stdio" -fi - -# Run in QEMU -if [[ ${QEMU_FLAGS} == *-S* ]]; then - info "Running OS in QEMU with GDB debugging enabled" - info "To connect GDB, run: gdb" - info "At the GDB prompt, type: target remote localhost:1234" -else - info "Running OS in QEMU..." -fi - -check_test_res() { - qemu_exit_code=$? - if [ $qemu_exit_code -eq 33 ]; then - # Success case (0x10 << 1) | 1 = 33 - info "All tests passed" - exit 0 - elif [ $qemu_exit_code -eq 35 ]; then - # Failure case (0x11 << 1) | 1 = 35 - warning "Some tests failed" - exit 1 - else - # Any other exit code is treated as a failure - warning "Some tests failed" - exit 1 - fi -} - - -kvm_flag="" - -trap 'check_test_res "tests completed"' ERR - cd "$project_root" qemu-system-x86_64 -M q35 \ ${kvm_flag} \ -cdrom "$build_dir/image.iso" \ -boot d \ - -m 2G \ + ${vm_memory_flag} \ ${serial_flags} \ -no-reboot \ - ${test_flags} \ - ${debug_flags} \ - ${QEMU_FLAGS:-} + ${boot_flags}