diff --git a/kernel-rs/Cargo.toml b/kernel-rs/Cargo.toml index 90aada02..9f28fcab 100644 --- a/kernel-rs/Cargo.toml +++ b/kernel-rs/Cargo.toml @@ -9,9 +9,13 @@ crate-type = ["staticlib"] [dependencies] rlibc = "1.0" bitflags = "1.0.1" +spin = "0.4" multiboot2 = { path = "multiboot2-elf64" } +slab_allocator = "0.3.1" x86 = { path = "x86" } [dependencies.lazy_static] version = "1.0.0" features = ["spin_no_std"] + + diff --git a/kernel-rs/Makefile b/kernel-rs/Makefile index 6e832f9d..7a9ed66b 100644 --- a/kernel-rs/Makefile +++ b/kernel-rs/Makefile @@ -1,77 +1,42 @@ SHELL := /bin/bash -ifeq ($(shell whoami), william) - PORT := 4242 - PORTG := 4244 -else - PORT := 4342 - PORTG := 4344 -endif +ARCH := x86 +OS := bluesnow +target ?= $(ARCH)-$(OS) -project := bluesnow -arch ?= x86 NASM := /usr/bin/nasm -f elf -g LD := /usr/bin/ld -m elf_i386 -L ./ -n --gc-sections -# QEMU := qemu-system-x86_64 -device isa-debug-exit,iobase=0xf4,iosize=0x04 -gdb tcp::$(PORTG) -enable-kvm -monitor telnet:127.0.0.1:$(PORT),server,nowait -QEMU := qemu-system-x86_64 -gdb tcp::$(PORTG) -enable-kvm -monitor telnet:127.0.0.1:$(PORT),server,nowait +MKDIR := mkdir -p -kerName := BlueSnow -kernel := build/$(kerName) +kernel := build/$(OS) iso := $(kernel).iso DIRISO := build/isofiles -target ?= $(arch)-$(project) -rust_os := target/$(target)/debug/lib$(project).a +rust_os := target/$(target)/debug/lib$(OS).a -linker_script := src/arch/$(arch)/linker.ld -grub.cfg := src/arch/$(arch)/grub.cfg -asm_source := $(wildcard src/arch/$(arch)/*.asm) -asm_object := $(patsubst src/arch/$(arch)/%.asm, build/arch/$(arch)/%.o, $(asm_source)) - -KERNEL_RUN := $(QEMU) -curses -cdrom $(iso) -MONITOR := sleep 0.5;\ - telnet 127.0.0.1 $(PORT);\ - kill \`ps -x | grep \"[g]db -q\" | cut -d \ -f 1 \`;\ - kill \`ps -x | grep \"[g]db -q\" | cut -d \ -f 2 \` -GDB := gdb -q\ - -ex \"set arch i386:x86-64\"\ - -ex \"file $(kernel)\"\ - -ex \"target remote :$(PORTG)\"\ - -ex \"continue\" +linker_script := src/arch/$(ARCH)/linker.ld +grub.cfg := src/arch/$(ARCH)/grub.cfg +asm_source := $(wildcard src/arch/$(ARCH)/*.asm) +asm_object := $(patsubst src/arch/$(ARCH)/%.asm, build/arch/$(ARCH)/%.o, $(asm_source)) all: $(kernel) -build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm Makefile - @mkdir -p $(shell dirname $@) +build/arch/$(ARCH)/%.o: src/arch/$(ARCH)/%.asm Makefile + @$(MKDIR) $(shell dirname $@) $(NASM) $< -o $@ $(kernel): $(rust_os) $(asm_object) $(linker_script) Makefile $(LD) -o $@ -T $(linker_script) $(asm_object) $(rust_os) $(iso): $(kernel) $(grub.cfg) Makefile - @mkdir -p $(DIRISO)/boot/grub + @$(MKDIR) $(DIRISO)/boot/grub @cp $(grub.cfg) $(DIRISO)/boot/grub - @cp $(kernel) $(DIRISO)/boot/$(kerName) + @cp $(kernel) $(DIRISO)/boot/$(OS) @grub-mkrescue -o $@ $(DIRISO) 2>/dev/null -# $(kernel).img: $(kernel) -# @cp .clean.img $@ -# @sudo mount -oloop=/dev/loop0,offset=32256 $@ /mnt -# @sudo cp $(kernel) /mnt/boot/. -# @sudo umount /mnt - -run: $(iso) Makefile - @tmux info >&- || { echo -e "\033[38;5;16m ~~ NOT IN A VALID TMUX SESSION ~~\033[0m" ; exit 1; } - @tmux new-window 'tmux split-window -h "$(MONITOR)"; tmux split-window -fv "$(GDB)"; tmux select-pane -t 1; tmux resize-pane -x 80 -y 25; $(KERNEL_RUN)' - -# Run without try to do a compile -R: - @tmux info >&- || { echo -e "\033[38;5;16m ~~ NOT IN A VALID TMUX SESSION ~~\033[0m" ; exit 1; } - @tmux new-window 'tmux split-window -h "$(MONITOR)"; tmux split-window -fv "$(GDB)"; tmux select-pane -t 1; tmux resize-pane -x 80 -y 25; $(KERNEL_RUN)' - clean: @xargo clean - @rm -r build + @rm -rf build $(rust_os): $(target).json Makefile @RUST_TARGET_PATH="$(shell pwd)" xargo build --target $(target) @@ -79,4 +44,7 @@ $(rust_os): $(target).json Makefile kernel: $(rust_os) iso: $(iso) -.PHONY: R run clean kernel iso $(rust_os) +.PHONY: clean kernel iso $(rust_os) + +# Emulation recipes +include mk/qemu.mk diff --git a/kernel-rs/mk/qemu.mk b/kernel-rs/mk/qemu.mk new file mode 100644 index 00000000..0ac107ab --- /dev/null +++ b/kernel-rs/mk/qemu.mk @@ -0,0 +1,25 @@ +ifeq ($(shell whoami), william) + PORT := 4242 + PORTG := 4244 +else + PORT := 4342 + PORTG := 4344 +endif + +QEMU := qemu-system-x86_64 +QEMUFLAGS := -gdb tcp::$(PORTG) -enable-kvm -monitor telnet:127.0.0.1:$(PORT),server,nowait -curses -cdrom + +MONITOR := sleep 0.5;\ + telnet 127.0.0.1 $(PORT);\ + kill \`ps -x | grep \"[g]db -q\" | cut -d \ -f 1 \`;\ + kill \`ps -x | grep \"[g]db -q\" | cut -d \ -f 2 \` +GDB := gdb -q\ + -ex \"set arch i386:x86-64\"\ + -ex \"file $(kernel)\"\ + -ex \"target remote :$(PORTG)\"\ + -ex \"continue\" + +qemu: + @tmux info >&- || { echo -e "\033[38;5;16mPlease run inside a tmux session\033[0m" ; exit 1; } + @tmux new-window 'tmux split-window -h "$(MONITOR)"; tmux split-window -fv "$(GDB)"; tmux select-pane -t 1; tmux resize-pane -x 80 -y 25; $(QEMU) $(QEMUFLAGS) $(iso)' + diff --git a/kernel-rs/src/allocator/mod.rs b/kernel-rs/src/allocator/mod.rs new file mode 100644 index 00000000..2a4ca765 --- /dev/null +++ b/kernel-rs/src/allocator/mod.rs @@ -0,0 +1,27 @@ +pub use self::slab::Allocator; +mod slab; + +use x86::*; +use x86::structures::paging::*; +use arch::x86::paging::*; + +fn map_heap(active_table: &mut ActivePageTable, offset: usize, size: usize) +{ + let heap_start_page = Page::containing_address(VirtAddr::new(offset as u32)); + let heap_end_page = Page::containing_address(VirtAddr::new( + offset as u32 + size as u32 - 1)); + + for page in heap_start_page..heap_end_page + 1 { + active_table.map(page, PageTableFlags::WRITABLE); + } +} + +/// should be called only once +pub unsafe fn init(active_table: &mut ActivePageTable) { + let offset = ::HEAP_START; + let size = ::HEAP_SIZE; + + map_heap(active_table, offset, size); + + Allocator::init(offset, size); +} diff --git a/kernel-rs/src/allocator/slab.rs b/kernel-rs/src/allocator/slab.rs new file mode 100644 index 00000000..ddc50501 --- /dev/null +++ b/kernel-rs/src/allocator/slab.rs @@ -0,0 +1,43 @@ +use alloc::heap::{Alloc, AllocErr, Layout}; +use spin::Mutex; +use slab_allocator::Heap; + +static HEAP: Mutex> = Mutex::new(None); + +pub struct Allocator; + +impl Allocator { + pub unsafe fn init(offset: usize, size: usize) { + *HEAP.lock() = Some(Heap::new(offset, size)); + } +} + +unsafe impl<'a> Alloc for &'a Allocator { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + if let Some(ref mut heap) = *HEAP.lock() { + heap.allocate(layout) + } else { + panic!("__rust_allocate: heap not initialized"); + } + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + if let Some(ref mut heap) = *HEAP.lock() { + heap.deallocate(ptr, layout) + } else { + panic!("__rust_deallocate: heap not initialized"); + } + } + + fn oom(&mut self, error: AllocErr) -> ! { + panic!("Out of memory: {:?}", error); + } + + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + if let Some(ref mut heap) = *HEAP.lock() { + heap.usable_size(layout) + } else { + panic!("__rust_usable_size: heap not initialized"); + } + } +} diff --git a/kernel-rs/src/arch/mod.rs b/kernel-rs/src/arch/mod.rs new file mode 100644 index 00000000..aebe1682 --- /dev/null +++ b/kernel-rs/src/arch/mod.rs @@ -0,0 +1 @@ +pub mod x86; diff --git a/kernel-rs/src/arch/x86/grub.cfg b/kernel-rs/src/arch/x86/grub.cfg index 4cbeea5c..7bccc42f 100644 --- a/kernel-rs/src/arch/x86/grub.cfg +++ b/kernel-rs/src/arch/x86/grub.cfg @@ -2,6 +2,6 @@ set timeout=0 set default=0 menuentry "Blue Snow" { - multiboot2 /boot/BlueSnow + multiboot2 /boot/bluesnow boot } diff --git a/kernel-rs/src/arch/x86/interrupt/exception.rs b/kernel-rs/src/arch/x86/interrupt/exception.rs new file mode 100644 index 00000000..40674263 --- /dev/null +++ b/kernel-rs/src/arch/x86/interrupt/exception.rs @@ -0,0 +1,34 @@ +// https://wiki.osdev.org/Exceptions + +use x86::structures::idt::*; + +interrupt!(divide_by_zero, {}); +interrupt!(debug, {}); +interrupt!(non_maskable, {}); +interrupt!(breakpoint, {}); +interrupt!(overflow, {}); +interrupt!(bound_range, {}); +interrupt!(invalid_opcode, {}); +interrupt!(device_not_available, {}); +interrupt_err!(double_fault, {}); +interrupt!(coprocessor_segment_overrun, {}); +interrupt_err!(invalid_tss, {}); +interrupt_err!(segment_not_present, {}); +interrupt_err!(stack_segment, {}); +interrupt_err!(general_protection, {}); + +pub extern "x86-interrupt" fn page_fault( + stack_frame: &mut ExceptionStackFrame, code: PageFaultErrorCode) +{ + println!("Exception: page_fault"); + println!("Error code: {:#b}", code); + println!("{:#?}", stack_frame); + flush!(); +} + +interrupt!(x87_fpu, {}); +interrupt_err!(alignment_check, {}); +interrupt!(machine_check, {}); +interrupt!(simd, {}); +interrupt!(virtualization, {}); +interrupt_err!(security, {}); diff --git a/kernel-rs/src/arch/x86/interrupt/irq.rs b/kernel-rs/src/arch/x86/interrupt/irq.rs new file mode 100644 index 00000000..3460f22d --- /dev/null +++ b/kernel-rs/src/arch/x86/interrupt/irq.rs @@ -0,0 +1,6 @@ +use x86::structures::idt::*; + +interrupt!(keyboard, { + println!("key pressed!"); + flush!(); +}); diff --git a/kernel-rs/src/arch/x86/interrupt/mod.rs b/kernel-rs/src/arch/x86/interrupt/mod.rs new file mode 100644 index 00000000..e024f5c2 --- /dev/null +++ b/kernel-rs/src/arch/x86/interrupt/mod.rs @@ -0,0 +1,46 @@ +use x86::structures::idt::*; + +#[macro_use] pub mod exception; +#[macro_use] pub mod irq; + + +lazy_static! { + static ref IDT: Idt = { + let mut idt = Idt::new(); + + // set up CPU exceptions + idt.breakpoint.set_handler_fn(exception::breakpoint); + idt.debug.set_handler_fn(exception::debug); + idt.non_maskable_interrupt.set_handler_fn(exception::non_maskable); + idt.breakpoint.set_handler_fn(exception::breakpoint); + idt.overflow.set_handler_fn(exception::overflow); + idt.bound_range_exceeded.set_handler_fn(exception::bound_range); + idt.invalid_opcode.set_handler_fn(exception::invalid_opcode); + idt.device_not_available.set_handler_fn(exception::device_not_available); + idt.double_fault.set_handler_fn(exception::double_fault); + idt.segment_not_present.set_handler_fn(exception::segment_not_present); + idt.stack_segment_fault.set_handler_fn(exception::stack_segment); + idt.general_protection_fault.set_handler_fn(exception::general_protection); + idt.page_fault.set_handler_fn(exception::page_fault); + idt.x87_floating_point.set_handler_fn(exception::x87_fpu); + idt.alignment_check.set_handler_fn(exception::alignment_check); + idt.machine_check.set_handler_fn(exception::machine_check); + idt.simd_floating_point.set_handler_fn(exception::simd); + idt.virtualization.set_handler_fn(exception::virtualization); + + // set up IRQs + idt.interrupts[0].set_handler_fn(irq::keyboard); + idt.interrupts[1].set_handler_fn(irq::keyboard); + idt.interrupts[2].set_handler_fn(irq::keyboard); + idt + }; +} + +// pub fn init(memory_controller: &mut ::memory::MemoryController) { +pub fn init() { + // let double_fault_stack = memory_controller.alloc_stack(1) + // .expect("could not allocate double fault stack"); + // println!("DF stack: {:#?}", double_fault_stack); + // flush!(); + IDT.load(); +} diff --git a/kernel-rs/src/arch/x86/macros.rs b/kernel-rs/src/arch/x86/macros.rs new file mode 100644 index 00000000..94b41202 --- /dev/null +++ b/kernel-rs/src/arch/x86/macros.rs @@ -0,0 +1,36 @@ +#[macro_export] +macro_rules! interrupt { + ($name:ident, $func:block) => { + pub extern "x86-interrupt" fn $name(stack_frame: &mut ExceptionStackFrame) + { + println!("Exception: {}", stringify!($name)); + println!("{:#?}", stack_frame); + flush!(); + + #[allow(unused_variables)] + fn inner(stack: &mut ExceptionStackFrame) { + $func + } + inner(stack_frame); + } + } +} + +#[macro_export] +macro_rules! interrupt_err { + ($name:ident, $func:block) => { + pub extern "x86-interrupt" fn $name( + stack_frame: &mut ExceptionStackFrame, _error_code: u32) + { + println!("Exception: {}", stringify!($name)); + println!("{:#?}", stack_frame); + flush!(); + + #[allow(unused_variables)] + fn inner(stack: &mut ExceptionStackFrame) { + $func + } + inner(stack_frame); + } + } +} diff --git a/kernel-rs/src/arch/x86/mod.rs b/kernel-rs/src/arch/x86/mod.rs new file mode 100644 index 00000000..da1e67cc --- /dev/null +++ b/kernel-rs/src/arch/x86/mod.rs @@ -0,0 +1,42 @@ +extern crate x86; + +#[macro_use] +pub mod macros; +pub mod paging; +pub mod interrupt; + +use multiboot2; +use acpi; + +#[no_mangle] +pub unsafe extern fn x86_rust_start(multiboot_info_addr: usize) { + // parse multiboot2 info + let boot_info = multiboot2::load(multiboot_info_addr); + + // ACPI must be intialized BEFORE paging is active + if let Some(rsdp) = boot_info.rsdp_v2_tag() { + acpi::load(rsdp).expect("ACPI failed"); + } else if let Some(rsdp) = boot_info.rsdp_tag() { + acpi::load(rsdp).expect("ACPI failed"); + } else { + acpi::init().expect("ACPI failed"); + } + + // set up physical allocator + ::memory::init(&boot_info); + + // set up virtual mapping + let mut active_table = self::paging::init(&boot_info); + + // set up heap + ::allocator::init(&mut active_table); + + // after core has loaded + ::memory::init_noncore(); + + // set up interrupts + self::interrupt::init(); + + // primary CPU entry point + ::kmain(); +} diff --git a/kernel-rs/src/memory/paging/mapper.rs b/kernel-rs/src/arch/x86/paging/mapper.rs similarity index 51% rename from kernel-rs/src/memory/paging/mapper.rs rename to kernel-rs/src/arch/x86/paging/mapper.rs index 319f8827..128e82be 100644 --- a/kernel-rs/src/memory/paging/mapper.rs +++ b/kernel-rs/src/arch/x86/paging/mapper.rs @@ -1,10 +1,9 @@ -use memory::{PAGE_SIZE, FrameAllocator}; use core::ptr::Unique; use x86::structures::paging::*; use x86::instructions::tlb; use x86::usize_conversions::usize_from; use x86::*; -use super::paging::table::RecTable; +use super::table::RecTable; // virtual address of recursively mapped P2 // for protected mode non PAE @@ -59,43 +58,38 @@ impl Mapper { } /// map a virtual page to a physical frame in the page tables - pub fn map_to(&mut self, page: Page, frame: PhysFrame, flags: PageTableFlags, - allocator: &mut A) - where A: FrameAllocator - { - let p2 = self.p2_mut(); - let p1 = p2.next_table_create(usize_from(u32::from(page.p2_index())), allocator); - assert!(p1[page.p1_index()].is_unused()); - p1[page.p1_index()].set(frame, flags | PageTableFlags::PRESENT); - } + pub fn map_to(&mut self, page: Page, frame: PhysFrame, flags: PageTableFlags) + { + let p2 = self.p2_mut(); + let p1 = p2.next_table_create(usize_from(u32::from(page.p2_index()))); + assert!(p1[page.p1_index()].is_unused()); + p1[page.p1_index()].set(frame, flags | PageTableFlags::PRESENT); + } - pub fn map(&mut self, page: Page, flags: PageTableFlags, allocator: &mut A) - where A: FrameAllocator - { - let frame = allocator.allocate_frame().expect("out of memory"); - self.map_to(page, frame, flags, allocator) - } + pub fn map(&mut self, page: Page, flags: PageTableFlags) + { + let frame = ::memory::allocate_frames(1).expect("out of frames"); + self.map_to(page, frame, flags) + } - pub fn identity_map(&mut self, frame: PhysFrame, flags: PageTableFlags, allocator: &mut A) - where A: FrameAllocator - { - let virt_addr = VirtAddr::new(frame.start_address().as_u32()); - let page = Page::containing_address(virt_addr); - self.map_to(page, frame, flags, allocator); - } + pub fn identity_map(&mut self, frame: PhysFrame, flags: PageTableFlags) + { + let virt_addr = VirtAddr::new(frame.start_address().as_u32()); + let page = Page::containing_address(virt_addr); + self.map_to(page, frame, flags); + } - pub fn unmap(&mut self, page: Page, allocator: &mut A) - where A: FrameAllocator - { - assert!(self.translate(page.start_address()).is_some()); + pub fn unmap(&mut self, page: Page) + { + assert!(self.translate(page.start_address()).is_some()); - let p1 = self.p2_mut() - .next_table_mut(usize_from(u32::from(page.p2_index()))) - .expect("mapping code does not support huge pages"); - let frame = p1[page.p1_index()].pointed_frame().unwrap(); - p1[page.p1_index()].set_unused(); - tlb::flush(page.start_address()); - // TODO - // allocator.deallocate_frame(frame); - } + let p1 = self.p2_mut() + .next_table_mut(usize_from(u32::from(page.p2_index()))) + .expect("mapping code does not support huge pages"); + let frame = p1[page.p1_index()].pointed_frame().unwrap(); + p1[page.p1_index()].set_unused(); + tlb::flush(page.start_address()); + // TODO + ::memory::deallocate_frames(frame, 1); + } } diff --git a/kernel-rs/src/memory/paging/mod.rs b/kernel-rs/src/arch/x86/paging/mod.rs similarity index 85% rename from kernel-rs/src/memory/paging/mod.rs rename to kernel-rs/src/arch/x86/paging/mod.rs index 5b3fbd67..1a4db1c7 100644 --- a/kernel-rs/src/memory/paging/mod.rs +++ b/kernel-rs/src/arch/x86/paging/mod.rs @@ -4,7 +4,7 @@ mod table; mod temporary_page; mod mapper; -use memory::*; +// use memory::*; use self::mapper::Mapper; use self::temporary_page::TemporaryPage; use core::ops::{Deref, DerefMut}; @@ -12,6 +12,19 @@ use multiboot2::BootInformation; use x86::*; use x86::registers::control::Cr3; use x86::instructions::tlb; +use x86::structures::paging::*; +use multiboot2; + +/// should be called only once +pub fn init(boot_info: &multiboot2::BootInformation) -> ActivePageTable { + use x86::registers::control::*; + Cr4::add(Cr4Flags::PSE); + Cr0::add(Cr0Flags::PAGING | Cr0Flags::WRITE_PROTECT); + + let active_table = remap_the_kernel(boot_info); + + active_table +} pub struct ActivePageTable { mapper: Mapper, @@ -92,27 +105,26 @@ impl InactivePageTable { } } -pub fn remap_the_kernel(allocator: &mut A, boot_info: &BootInformation) - -> ActivePageTable - where A: FrameAllocator +pub fn remap_the_kernel(boot_info: &BootInformation) -> ActivePageTable { - let mut temporary_page = TemporaryPage::new(Page{number: 0xcafe}, allocator); + let mut temporary_page = TemporaryPage::new(Page{number: 0xcafe}); let mut active_table = unsafe { ActivePageTable::new() }; let mut new_table = { - let frame = allocator.allocate_frame().expect("no more frames"); + let frame = ::memory::allocate_frames(1).expect("no more frames"); InactivePageTable::new(frame, &mut active_table, &mut temporary_page) }; active_table.with(&mut new_table, &mut temporary_page, |mapper| { - // identity map the VGA text buffer + // id map vga buffer let vga_buffer_frame = PhysFrame::containing_address(PhysAddr::new(0xb8000)); - mapper.identity_map(vga_buffer_frame, PageTableFlags::WRITABLE, allocator); + mapper.identity_map(vga_buffer_frame, PageTableFlags::WRITABLE); let elf_sections_tag = boot_info.elf_sections_tag() .expect("Memory map tag required"); + // id map kernel sections for section in elf_sections_tag.sections() { if !section.is_allocated() { continue; @@ -126,16 +138,17 @@ pub fn remap_the_kernel(allocator: &mut A, boot_info: &BootInformation) let end_frame = PhysFrame::containing_address( PhysAddr::new(section.end_address() as u32 - 1)); for frame in start_frame..end_frame + 1 { - mapper.identity_map(frame, flags, allocator); + mapper.identity_map(frame, flags); } } + // id map multiboot let multiboot_start = PhysFrame::containing_address( PhysAddr::new(boot_info.start_address() as u32)); let multiboot_end = PhysFrame::containing_address( PhysAddr::new(boot_info.end_address() as u32 - 1)); for frame in multiboot_start..multiboot_end + 1 { - mapper.identity_map(frame, PageTableFlags::PRESENT, allocator); + mapper.identity_map(frame, PageTableFlags::PRESENT); } }); @@ -144,7 +157,7 @@ pub fn remap_the_kernel(allocator: &mut A, boot_info: &BootInformation) let old_p2_page = Page::containing_address( VirtAddr::new(old_table.p2_frame.start_address().as_u32())); - active_table.unmap(old_p2_page, allocator); + active_table.unmap(old_p2_page); active_table } @@ -163,10 +176,6 @@ fn elf_to_pagetable_flags(elf_flags: &multiboot2::ElfSectionFlags) if elf_flags.contains(ElfSectionFlags::WRITABLE) { flags = flags | PageTableFlags::WRITABLE; } - // LONG MODE STUFF - // if !elf_flags.contains(ELF_SECTION_EXECUTABLE) { - // flags = flags | PageTableFlags::NO_EXECUTE; - // } flags } diff --git a/kernel-rs/src/memory/paging/table.rs b/kernel-rs/src/arch/x86/paging/table.rs similarity index 53% rename from kernel-rs/src/memory/paging/table.rs rename to kernel-rs/src/arch/x86/paging/table.rs index 70a9731e..41eec7e0 100644 --- a/kernel-rs/src/memory/paging/table.rs +++ b/kernel-rs/src/arch/x86/paging/table.rs @@ -1,15 +1,12 @@ -use memory::*; use x86::structures::paging::*; -// use x86::ux::*; pub trait RecTable { fn next_table_address(&self, index: usize) -> Option; fn next_table(&self, index: usize) -> Option<&PageTable>; fn next_table_mut(&mut self, index: usize) -> Option<&mut PageTable>; - fn next_table_create(&mut self, - index: usize, - allocator: &mut A) + fn next_table_create(&mut self, + index: usize) -> &mut PageTable; } @@ -35,18 +32,17 @@ impl RecTable for PageTable .map(|address| unsafe { &mut *(address as *mut _) }) } - fn next_table_create(&mut self, - index: usize, - allocator: &mut A) -> &mut PageTable - where A: FrameAllocator - { - if self.next_table(index).is_none() { - assert!(!self[index].flags().contains(PageTableFlags::HUGE_PAGE), - "mapping code does not support huge pages"); - let frame = allocator.allocate_frame().expect("no frames available"); - self[index].set(frame, PageTableFlags::PRESENT | PageTableFlags::WRITABLE); - self.next_table_mut(index).expect("next_table_mut gave None").zero() - } - self.next_table_mut(index).expect("no next table 2") + fn next_table_create(&mut self, + index: usize) -> &mut PageTable + { + if self.next_table(index).is_none() { + assert!(!self[index].flags().contains(PageTableFlags::HUGE_PAGE), + "mapping code does not support huge pages"); + let frame = ::memory::allocate_frames(1) + .expect("out of memory"); + self[index].set(frame, PageTableFlags::PRESENT | PageTableFlags::WRITABLE); + self.next_table_mut(index).expect("next_table_mut gave None").zero() } + self.next_table_mut(index).expect("no next table 2") + } } diff --git a/kernel-rs/src/memory/paging/temporary_page.rs b/kernel-rs/src/arch/x86/paging/temporary_page.rs similarity index 51% rename from kernel-rs/src/memory/paging/temporary_page.rs rename to kernel-rs/src/arch/x86/paging/temporary_page.rs index c7e9b094..becdb0fc 100644 --- a/kernel-rs/src/memory/paging/temporary_page.rs +++ b/kernel-rs/src/arch/x86/paging/temporary_page.rs @@ -1,22 +1,16 @@ use super::ActivePageTable; -use memory::{FrameAllocator}; use x86::*; use x86::structures::paging::*; pub struct TemporaryPage { pub page: Page, - allocator: TinyAllocator, } impl TemporaryPage { - pub fn new(page: Page, allocator: &mut A) -> TemporaryPage - where A: FrameAllocator - { - TemporaryPage { - page: page, - allocator: TinyAllocator::new(allocator), - } - } + pub fn new(page: Page) -> TemporaryPage + { + TemporaryPage { page: page } + } /// Maps the temporary page to the given frame in the active table. /// Returns the start address of the temporary page. @@ -25,7 +19,7 @@ impl TemporaryPage { { assert!(active_table.translate_page(self.page).is_none(), "temporary page is already mapped"); - active_table.map_to(self.page, frame, PageTableFlags::WRITABLE, &mut self.allocator); + active_table.map_to(self.page, frame, PageTableFlags::WRITABLE); // this kind of check should be done in a test routine assert!(active_table.translate_page(self.page).is_some(), "temporary page was not mapped"); @@ -34,7 +28,7 @@ impl TemporaryPage { /// Unmaps the temporary page in the active table. pub fn unmap(&mut self, active_table: &mut ActivePageTable) { - active_table.unmap(self.page, &mut self.allocator) + active_table.unmap(self.page) } /// Maps the temporary page to the given page table frame in the active @@ -46,36 +40,3 @@ impl TemporaryPage { unsafe { &mut *(self.map(frame, active_table).as_u32() as *mut PageTable) } } } - -struct TinyAllocator([Option; 1]); - -impl TinyAllocator { - fn new(allocator: &mut A) -> TinyAllocator - where A: FrameAllocator - { - let mut f = || allocator.allocate_frame(); - let frames = [f()]; - TinyAllocator(frames) - } -} - -impl FrameAllocator for TinyAllocator { - fn allocate_frame(&mut self) -> Option { - for frame_option in &mut self.0 { - if frame_option.is_some() { - return frame_option.take(); - } - } - None - } - - fn deallocate_frame(&mut self, frame: PhysFrame) { - for frame_option in &mut self.0 { - if frame_option.is_none() { - *frame_option = Some(frame); - return; - } - } - panic!("Tiny allocator can only hold 1 frame."); - } -} diff --git a/kernel-rs/src/arch/x86/start.asm b/kernel-rs/src/arch/x86/start.asm index 8c69f1ce..45ec32c2 100644 --- a/kernel-rs/src/arch/x86/start.asm +++ b/kernel-rs/src/arch/x86/start.asm @@ -1,5 +1,5 @@ global x86_start -extern kmain +extern x86_rust_start section .text bits 32 @@ -11,7 +11,7 @@ x86_start: mov fs, ax mov gs, ax - call kmain + call x86_rust_start cli ; clear interrupt HALT: diff --git a/kernel-rs/src/interrupts/mod.rs b/kernel-rs/src/interrupts/mod.rs deleted file mode 100644 index aba00559..00000000 --- a/kernel-rs/src/interrupts/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -use x86::structures::idt::*; - -lazy_static! { - static ref IDT: Idt = { - let mut idt = Idt::new(); - idt.breakpoint.set_handler_fn(breakpoint_handler); - idt.double_fault.set_handler_fn(double_fault_handler); - // int #1 is keyboard - idt.interrupts[1].set_handler_fn(::keyboard::kbd_callback); - idt - }; -} - -pub fn init(memory_controller: &mut ::memory::MemoryController) { - let double_fault_stack = memory_controller.alloc_stack(1) - .expect("could not allocate double fault stack"); - // println!("DF stack: {:#?}", double_fault_stack); - // flush!(); - IDT.load(); -} - -extern "x86-interrupt" fn double_fault_handler( - stack_frame: &mut ExceptionStackFrame, _error_code: u32) -{ - println!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); - flush!(); -} - -extern "x86-interrupt" fn breakpoint_handler( - stack_frame: &mut ExceptionStackFrame) -{ - println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); - flush!(); -} diff --git a/kernel-rs/src/keyboard.rs b/kernel-rs/src/keyboard.rs index de6859db..c13360d4 100644 --- a/kernel-rs/src/keyboard.rs +++ b/kernel-rs/src/keyboard.rs @@ -1,5 +1,4 @@ extern crate core; -use x86::structures::idt::*; use cpuio; use vga; @@ -77,8 +76,7 @@ fn check_key_state(key: u8) -> (bool, usize) { } } -pub extern "x86-interrupt" fn kbd_callback( - stack_frame: &mut ExceptionStackFrame) { +pub fn kbd_callback() { println!("kbd callback called"); flush!(); static mut SHIFT: bool = false; diff --git a/kernel-rs/src/lib.rs b/kernel-rs/src/lib.rs index cb08eff0..5bb68a28 100644 --- a/kernel-rs/src/lib.rs +++ b/kernel-rs/src/lib.rs @@ -1,5 +1,6 @@ //! project hosted on [github](https://github.com/jzck/kernel) - +//! exclusively x86 +//! #![no_std] #![feature(lang_items)] #![feature(const_fn)] @@ -13,12 +14,16 @@ #![feature(abi_x86_interrupt)] extern crate rlibc; -extern crate multiboot2; -#[macro_use] extern crate lazy_static; #[macro_use] extern crate alloc; +#[macro_use] extern crate lazy_static; +extern crate spin; +extern crate multiboot2; +extern crate slab_allocator; + +// used by arch/x86, need conditional compilation here extern crate x86; -/// 80x25 screen and simplistic terminal driver +/// 80x25 terminal driver #[macro_use] pub mod vga; /// PS/2 detection and processing pub mod keyboard; @@ -26,40 +31,21 @@ pub mod keyboard; pub mod console; /// rust wrappers around cpu I/O instructions. pub mod cpuio; -/// ACPI self-content module +/// ACPI self contained module pub mod acpi; -/// physical frame allocator + paging module + heap allocator +/// Heap allocators +pub mod allocator; +/// Memory management pub mod memory; -/// x86 interruptions -pub mod interrupts; +/// arch specific entry points +pub mod arch; -fn init_kernel(multiboot_info_addr: usize) -> Result <(), &'static str> { +/// kernel entry point. arch module is responsible for calling this +pub fn kmain() -> ! { - let boot_info = unsafe { multiboot2::load(multiboot_info_addr)}; - - // ACPI must be intialized BEFORE paging (memory::init()) is active - if let Some(rsdp) = boot_info.rsdp_v2_tag() { - acpi::load(rsdp)?; - } else if let Some(rsdp) = boot_info.rsdp_tag() { - acpi::load(rsdp)?; - } else { - acpi::init()?; - } - - let mut memory_controller = memory::init(&boot_info); - interrupts::init(&mut memory_controller); + // vga is specific to chipset not cpu vga::init(); - Ok(()) -} - -#[no_mangle] -pub extern fn kmain(multiboot_info_addr: usize) -> ! { - if let Err(msg) = init_kernel(multiboot_info_addr) { - println!("Kernel initialization has failed: {}", msg); - cpuio::halt(); - } - // x86::instructions::interrupts::int3(); // fn stack_overflow() { stack_overflow(); } @@ -69,13 +55,13 @@ pub extern fn kmain(multiboot_info_addr: usize) -> ! { // *(0xdead as *mut u32) = 42; // }; + println!("at main now!"); + loop {} } #[lang = "eh_personality"] #[no_mangle] -pub extern fn eh_personality() { - -} +pub extern fn eh_personality() {} #[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32) @@ -87,11 +73,8 @@ pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32 loop {} } -use memory::BumpAllocator; - pub const HEAP_START: usize = (1 << 22); //first entry of p2 -pub const HEAP_SIZE: usize = 100 * 1024; //100 KiB +pub const HEAP_SIZE: usize = 10 * 4096 * 8; //~ 100 KiB #[global_allocator] -static HEAP_ALLOCATOR: BumpAllocator = BumpAllocator::new(HEAP_START, - HEAP_START + HEAP_SIZE); +static HEAP_ALLOCATOR: allocator::Allocator = allocator::Allocator; diff --git a/kernel-rs/src/memory/area_allocator.rs b/kernel-rs/src/memory/bump.rs similarity index 62% rename from kernel-rs/src/memory/area_allocator.rs rename to kernel-rs/src/memory/bump.rs index 6a24bb94..d45e595c 100644 --- a/kernel-rs/src/memory/area_allocator.rs +++ b/kernel-rs/src/memory/bump.rs @@ -1,8 +1,9 @@ -use memory::*; use multiboot2::{MemoryAreaIter, MemoryArea}; use x86::*; +use x86::structures::paging::PhysFrame; +use super::FrameAllocator; -pub struct AreaFrameAllocator { +pub struct BumpFrameAllocator { next_free_frame: PhysFrame, current_area: Option<&'static MemoryArea>, areas: MemoryAreaIter, @@ -12,11 +13,11 @@ pub struct AreaFrameAllocator { multiboot_end: PhysFrame, } -impl AreaFrameAllocator { +impl BumpFrameAllocator { pub fn new(kernel_start: usize, kernel_end: usize, multiboot_start: usize, multiboot_end: usize, - memory_areas: MemoryAreaIter) -> AreaFrameAllocator { - let mut allocator = AreaFrameAllocator { + memory_areas: MemoryAreaIter) -> BumpFrameAllocator { + let mut allocator = BumpFrameAllocator { next_free_frame: PhysFrame { number: 0 }, current_area: None, areas: memory_areas, @@ -49,37 +50,42 @@ impl AreaFrameAllocator { } } -impl FrameAllocator for AreaFrameAllocator { - fn allocate_frame(&mut self) -> Option { +impl FrameAllocator for BumpFrameAllocator { + fn allocate_frames(&mut self, count: usize) -> Option { + if count == 0 { return None }; if let Some(area) = self.current_area { - let frame = PhysFrame { number: self.next_free_frame.number }; + let start_frame = PhysFrame { number: self.next_free_frame.number }; + let end_frame = PhysFrame { number: self.next_free_frame.number + count as u32 - 1 }; + let current_area_last_frame = PhysFrame::containing_address( PhysAddr::new(area.end_address() as u32)); - if frame > current_area_last_frame { + if end_frame > current_area_last_frame { // all frames are taken in this area self.choose_next_area(); - } else if frame >= self.kernel_start && frame <= self.kernel_end { + } else if (start_frame >= self.kernel_start && start_frame <= self.kernel_end) || (end_frame >= self.kernel_start && end_frame <= self.kernel_end) { // frame used by kernel self.next_free_frame = PhysFrame { number: self.kernel_end.number + 1, - } - } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + }; + } else if (start_frame >= self.multiboot_start && start_frame <= self.multiboot_end) || (end_frame >= self.multiboot_start && end_frame <= self.multiboot_end) { // frame used by multiboot self.next_free_frame = PhysFrame { number: self.multiboot_end.number + 1, - } + }; } else { - self.next_free_frame.number += 1; - return Some(frame); + self.next_free_frame.number += count as u32; + return Some(start_frame); } // try again with next_free_frame - self.allocate_frame() + self.allocate_frames(count) } else { None } } - fn deallocate_frame(&mut self, frame: PhysFrame) { - unimplemented!(); + fn deallocate_frames(&mut self, frame: PhysFrame, count: usize) { + // bump doesnt deallocate, must be used inside of a recycler + println!("lost frames {:#x} ({})", frame.start_address().as_u32(), count); + // unimplemented!(); } } diff --git a/kernel-rs/src/memory/heap_allocator.rs b/kernel-rs/src/memory/heap_allocator.rs deleted file mode 100644 index d6ed05b6..00000000 --- a/kernel-rs/src/memory/heap_allocator.rs +++ /dev/null @@ -1,60 +0,0 @@ -use alloc::heap::{Alloc, AllocErr, Layout}; -use core::sync::atomic::{AtomicUsize, Ordering}; - -#[derive(Debug)] -pub struct BumpAllocator { - heap_start: usize, - heap_end: usize, - next: AtomicUsize, -} - -impl BumpAllocator { - pub const fn new(heap_start: usize, heap_end: usize) -> Self { - Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) } - } -} - -unsafe impl<'a> Alloc for &'a BumpAllocator { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - loop { - // load current state of the `next` field - let current_next = self.next.load(Ordering::Relaxed); - let alloc_start = align_up(current_next, layout.align()); - let alloc_end = alloc_start.saturating_add(layout.size()); - - if alloc_end <= self.heap_end { - // update the `next` pointer if it still has the value `current_next` - let next_now = self.next.compare_and_swap(current_next, alloc_end, - Ordering::Relaxed); - if next_now == current_next { - return Ok(alloc_start as *mut u8); - } - } else { - return Err(AllocErr::Exhausted{ request: layout }) - } - } - } - - unsafe fn dealloc(&mut self, pt: *mut u8, layout: Layout) { - // TODO - // do nothing, leak memory - } -} - -/// Align downwards. Returns the greatest x with alignment `align` -/// so that x <= addr. The alignment must be a power of 2. -pub fn align_down(addr: usize, align: usize) -> usize { - if align.is_power_of_two() { - addr & !(align - 1) - } else if align == 0 { - addr - } else { - panic!("`align` must be a power of 2"); - } -} - -/// Align upwards. Returns the smallest x with alignment `align` -/// so that x >= addr. The alignment must be a power of 2. -pub fn align_up(addr: usize, align: usize) -> usize { - align_down(addr + align - 1, align) -} diff --git a/kernel-rs/src/memory/mod.rs b/kernel-rs/src/memory/mod.rs index aad7c4ca..01514299 100644 --- a/kernel-rs/src/memory/mod.rs +++ b/kernel-rs/src/memory/mod.rs @@ -1,44 +1,26 @@ -mod area_allocator; -mod heap_allocator; -mod stack_allocator; -mod paging; +mod bump; +mod recycle; -pub use self::area_allocator::*; -pub use self::heap_allocator::*; -pub use self::stack_allocator::*; -pub use self::paging::remap_the_kernel; use multiboot2; -use x86::*; use x86::structures::paging::*; +use spin::Mutex; + +use self::bump::BumpFrameAllocator; +use self::recycle::RecycleAllocator; pub trait FrameAllocator { - fn allocate_frame(&mut self) -> Option; - fn deallocate_frame(&mut self, frame: PhysFrame); + fn allocate_frames(&mut self, size: usize) -> Option; + fn deallocate_frames(&mut self, frame: PhysFrame, size: usize); } -pub struct MemoryController { - active_table: paging::ActivePageTable, - frame_allocator: AreaFrameAllocator, - stack_allocator: StackAllocator, +pub struct MemoryControler { + frame_allocator: RecycleAllocator, + // stack_allocator: StackAllocator, } -impl MemoryController { - pub fn alloc_stack(&mut self, size_in_pages: usize) -> Option { - let &mut MemoryController { ref mut active_table, - ref mut frame_allocator, - ref mut stack_allocator } = self; - - stack_allocator.alloc_stack(active_table, frame_allocator, - size_in_pages) - } -} - -/// memory initialisation should only be called once -pub fn init(boot_info: &multiboot2::BootInformation) -> MemoryController { - use x86::registers::control::{Cr0, Cr4, Cr0Flags, Cr4Flags}; - Cr4::add(Cr4Flags::PSE); - Cr0::add(Cr0Flags::PAGING | Cr0Flags::WRITE_PROTECT); +static MEMORY_CONTROLER: Mutex> = Mutex::new(None); +pub fn init(boot_info: &multiboot2::BootInformation) { let elf_sections_tag = boot_info.elf_sections_tag().unwrap(); let memory_map_tag = boot_info.memory_map_tag().unwrap(); @@ -52,34 +34,48 @@ pub fn init(boot_info: &multiboot2::BootInformation) -> MemoryController { .map(|s| s.start_address() + s.size()) .max().unwrap(); - let mut frame_allocator = self::AreaFrameAllocator::new( + let bump_allocator = BumpFrameAllocator::new( kernel_start as usize, kernel_end as usize, boot_info.start_address(), boot_info.end_address(), memory_map_tag.memory_areas()); - let mut active_table = paging::remap_the_kernel(&mut frame_allocator, - boot_info); - use {HEAP_START, HEAP_SIZE}; + let frame_allocator = RecycleAllocator::new(bump_allocator); - let heap_start_page = Page::containing_address( - VirtAddr::new(HEAP_START as u32)); - let heap_end_page = Page::containing_address( - VirtAddr::new(HEAP_START as u32 + HEAP_SIZE as u32 - 1)); + // let stack_allocator = { + // let stack_alloc_start = heap_end_page + 1; + // let stack_alloc_end = stack_alloc_start + 100; + // let stack_alloc_range = stack_alloc_start..stack_alloc_end + 1; + // StackAllocator::new(stack_alloc_range) + // }; - for page in heap_start_page..heap_end_page { - active_table.map(page, PageTableFlags::WRITABLE, &mut frame_allocator); - } - - let stack_allocator = { - let stack_alloc_start = heap_end_page + 1; - let stack_alloc_end = stack_alloc_start + 100; - let stack_alloc_range = stack_alloc_start..stack_alloc_end + 1; - StackAllocator::new(stack_alloc_range) - }; - - MemoryController { - active_table, + *MEMORY_CONTROLER.lock() = Some(MemoryControler { frame_allocator, - stack_allocator, + // stack_allocator, + }); +} + +pub fn allocate_frames(count: usize) -> Option { + if let Some(ref mut controler) = *MEMORY_CONTROLER.lock() { + controler.frame_allocator.allocate_frames(count) + } else { + panic!("frame allocator not initialized!"); + } +} + +pub fn deallocate_frames(frame: PhysFrame, count: usize) { + if let Some(ref mut controler) = *MEMORY_CONTROLER.lock() { + controler.frame_allocator.deallocate_frames(frame, count) + } else { + panic!("frame allocator not initialized!"); + } +} + +/// Init memory module after core +/// Must be called once, and only once, +pub unsafe fn init_noncore() { + if let Some(ref mut controler) = *MEMORY_CONTROLER.lock() { + controler.frame_allocator.set_core(true); + } else { + panic!("frame allocator not initialized"); } } diff --git a/kernel-rs/src/memory/recycle.rs b/kernel-rs/src/memory/recycle.rs new file mode 100644 index 00000000..6cebe2b4 --- /dev/null +++ b/kernel-rs/src/memory/recycle.rs @@ -0,0 +1,105 @@ +//! Recycle allocator +//! Uses freed frames if possible, then uses inner allocator + +use alloc::Vec; +use x86::*; +use x86::structures::paging::*; +use super::*; + +pub struct RecycleAllocator { + inner: T, + core: bool, + free: Vec<(usize, usize)>, +} + +impl RecycleAllocator { + pub fn new(inner: T) -> Self { + Self { + inner: inner, + core: true, + free: Vec::new(), + } + } + + pub fn set_core(&mut self, core: bool) { + self.core = core; + } + + fn merge(&mut self, address: usize, count: usize) -> bool { + for i in 0 .. self.free.len() { + let changed = { + let free = &mut self.free[i]; + if address + count * 4096 == free.0 { + free.0 = address; + free.1 += count; + true + } else if free.0 + free.1 * 4096 == address { + free.1 += count; + true + } else { + false + } + }; + + if changed { + //TODO: Use do not use recursion + let (address, count) = self.free[i]; + if self.merge(address, count) { + self.free.remove(i); + } + return true; + } + } + + false + } +} + +impl FrameAllocator for RecycleAllocator { + fn allocate_frames(&mut self, count: usize) -> Option { + let mut small_i = None; + { + let mut small = (0, 0); + for i in 0..self.free.len() { + let free = self.free[i]; + // Later entries can be removed faster + if free.1 >= count { + if free.1 <= small.1 || small_i.is_none() { + small_i = Some(i); + small = free; + } + } + } + } + + if let Some(i) = small_i { + let (address, remove) = { + let free = &mut self.free[i]; + free.1 -= count; + (free.0 + free.1 * 4096, free.1 == 0) + }; + + if remove { + self.free.remove(i); + } + + //println!("Restoring frame {:?}, {}", frame, count); + Some(PhysFrame::containing_address(PhysAddr::new(address as u32))) + } else { + //println!("No saved frames {}", count); + self.inner.allocate_frames(count) + } + } + + fn deallocate_frames(&mut self, frame: PhysFrame, count: usize) { + // we cant use vec! before the heap has been initialized + if self.core { + self.inner.deallocate_frames(frame, count); + } else { + let address = frame.start_address().as_u32() as usize; + if ! self.merge(address, count) { + self.free.push((address, count)); + } + } + } +} diff --git a/kernel-rs/src/vga/mod.rs b/kernel-rs/src/vga/mod.rs index dc87c0d9..b773b4ff 100644 --- a/kernel-rs/src/vga/mod.rs +++ b/kernel-rs/src/vga/mod.rs @@ -123,6 +123,7 @@ impl Writer { self.buffer[i] = b' '; self.buffer[i + 1] = 0; self.flush(); + // flush!(); } pub fn write_byte(&mut self, byte: u8) { @@ -179,7 +180,6 @@ impl Writer { for col in 0..BUFFER_COLS/2 { self.buffer[((BUFFER_ROWS - 1) * BUFFER_COLS) + (col * 2)] = b' '; - self.buffer[((BUFFER_ROWS - 1) * BUFFER_COLS) + (col * 2) + 1] = 0; } self.buffer_pos = (BUFFER_ROWS - 1) * BUFFER_COLS; @@ -214,5 +214,7 @@ pub fn init() { format_args!("{: ^80}", r#" | : ;| : .' "#), format_args!("{: ^80}", r#" ' ,/ ; | .' "#), format_args!("{: ^80}", r#" '--' `---' "#)); + set_color!(); unsafe { VGA.prompt(); } + unsafe { VGA.flush(); } } diff --git a/kernel-rs/x86 b/kernel-rs/x86 index da544c83..3cef2945 160000 --- a/kernel-rs/x86 +++ b/kernel-rs/x86 @@ -1 +1 @@ -Subproject commit da544c834b3f3d8b53f456c48b3aafa58d09198a +Subproject commit 3cef2945c884857a6ef44c942a3d949790792db7