//! Writing an [OS] in Rust //! //! [os]: https://os.phil-opp.com #![no_std] #![no_main] #![feature(custom_test_frameworks)] #![test_runner(rustos::test_runner)] #![reexport_test_harness_main = "test_main"] extern crate bootloader; extern crate rustos; extern crate x86_64; use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; use rustos::println; use x86_64::{ structures::paging::{MapperAllSizes, Page}, VirtAddr, }; entry_point!(start_kernel); fn start_kernel(boot_info: &'static BootInfo) -> ! { println!("Welcome to the real world!"); // virtual to physical mapper. let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset); let mut mapper = unsafe { init(phys_mem_offset) }; let addresses = [ // the identity-mapped vga buffer page. 0xb8000, // some code page. 0x0020_1008, // some stack page. 0x0100_0020_1a10, // virtual address mapped to physical address 0. boot_info.physical_memory_offset, ]; // dump the virtual to physical mapping. for &address in &addresses { let virt = VirtAddr::new(address); let phys = mapper.translate_addr(virt); println!("{:?} -> {:?}", virt, phys); } // write the string "New!" to the screen through the new empty mapping! let mut frame_allocator = EmptyFrameAllocator; let page = Page::containing_address(VirtAddr::new(0)); create_example_mapping(page, &mut mapper, &mut frame_allocator); let page_ptr: *mut u64 = page.start_address().as_mut_ptr(); unsafe { page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e) }; // Let's map a frame with BootInfoFrameAllocator! let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; let page = Page::containing_address(VirtAddr::new(0xffff_ffff)); create_example_mapping(page, &mut mapper, &mut frame_allocator); let page_ptr: *mut u64 = page.start_address().as_mut_ptr(); unsafe { page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e) }; #[cfg(test)] test_main(); println!("It did not crash!!!"); rustos::hlt_loop(); } #[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); rustos::hlt_loop(); } #[cfg(test)] #[panic_handler] fn panic(info: &PanicInfo) -> ! { rustos::test_panic_handler(info) } use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; use x86_64::{ structures::paging::{ FrameAllocator, Mapper, OffsetPageTable, PageTable, PhysFrame, Size4KiB, UnusedPhysFrame, }, PhysAddr, }; unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { let level_4_table = active_level_4_table(physical_memory_offset); OffsetPageTable::new(level_4_table, physical_memory_offset) } unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { use x86_64::registers::control::Cr3; let (level_4_table_frame, _) = Cr3::read(); let phys = level_4_table_frame.start_address(); let virt = physical_memory_offset + phys.as_u64(); let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); &mut *page_table_ptr } fn create_example_mapping( page: Page, mapper: &mut OffsetPageTable, frame_allocator: &mut impl FrameAllocator, ) { use x86_64::structures::paging::PageTableFlags as Flags; let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000)); let unused_frame = unsafe { UnusedPhysFrame::new(frame) }; let flags = Flags::PRESENT | Flags::WRITABLE; let map_to_result = mapper.map_to(page, unused_frame, flags, frame_allocator); map_to_result.expect("map_to failed").flush(); } struct BootInfoFrameAllocator { memory_map: &'static MemoryMap, next: usize, } unsafe impl FrameAllocator for BootInfoFrameAllocator { fn allocate_frame(&mut self) -> Option { let frame = self.usable_frames().nth(self.next); self.next += 1; frame } } impl BootInfoFrameAllocator { unsafe fn init(memory_map: &'static MemoryMap) -> Self { Self { memory_map, next: 0, } } fn usable_frames(&self) -> impl Iterator { let regions = self.memory_map.iter(); let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable); let addr_ranges = usable_regions.map(|r| r.range.start_addr()..r.range.end_addr()); let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); let frames = frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))); frames.map(|f| unsafe { UnusedPhysFrame::new(f) }) } } struct EmptyFrameAllocator; unsafe impl FrameAllocator for EmptyFrameAllocator { fn allocate_frame(&mut self) -> Option { None } }