├── .gitignore ├── amd64 ├── unifont.font ├── boot │ ├── grub.cfg │ ├── long_mode_start.asm │ ├── multiboot_header.asm │ └── boot.asm ├── rdtsc.rs ├── linker.ld ├── fpu.rs ├── asm.rs ├── cpuid.rs ├── Cargo.toml ├── mem.rs ├── serial.rs ├── rtc.rs ├── gdt.rs ├── idt.rs ├── pspeaker.rs ├── keyboard.rs ├── mouse.rs ├── pic.rs ├── pci.rs ├── ata.rs ├── main.rs ├── sb16.rs ├── Cargo.lock └── graphics.rs ├── Cargo.toml ├── .cargo ├── config └── x86_64-kernel.json ├── .gitmodules ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | build/ 3 | *.o 4 | *.iso 5 | *.elf 6 | *.img 7 | -------------------------------------------------------------------------------- /amd64/unifont.font: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/littledivy/rkernel/HEAD/amd64/unifont.font -------------------------------------------------------------------------------- /amd64/boot/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "my os" { 5 | multiboot2 /boot/kernel.bin 6 | boot 7 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "amd64/" 4 | ] 5 | 6 | [profile.dev] 7 | panic = "abort" 8 | 9 | [profile.release] 10 | panic = "abort" -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core", "compiler_builtins", "alloc"] 3 | build-std-features = ["compiler-builtins-mem"] 4 | 5 | [build] 6 | target = ".cargo/x86_64-kernel.json" 7 | -------------------------------------------------------------------------------- /amd64/rdtsc.rs: -------------------------------------------------------------------------------- 1 | pub fn rdtsc() -> f64 { 2 | unsafe { 3 | core::arch::x86_64::_mm_lfence(); 4 | core::arch::x86_64::_rdtsc() as f64 5 | } 6 | } 7 | 8 | pub fn delta_ns(start: f64) -> f64 { 9 | (rdtsc() - start - 10f64) * 2300000000000f64 10 | } 11 | -------------------------------------------------------------------------------- /amd64/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | 3 | SECTIONS { 4 | . = 1M; 5 | 6 | .boot : 7 | { 8 | /* ensure that the multiboot header is at the beginning */ 9 | KEEP(*(.multiboot_header)) 10 | } 11 | 12 | .text : 13 | { 14 | *(.text) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ps2-mouse"] 2 | path = amd64/ps2-mouse 3 | url = git@github.com:littledivy/ps2-mouse.git 4 | [submodule "ext2-rs"] 5 | path = amd64/ext2-rs 6 | url = git@github.com:littledivy/ext2-rs.git 7 | [submodule "vga"] 8 | path = amd64/vga 9 | url = https://github.com/ethindp/vga 10 | -------------------------------------------------------------------------------- /amd64/boot/long_mode_start.asm: -------------------------------------------------------------------------------- 1 | global long_mode_start 2 | extern _start 3 | 4 | section .text 5 | bits 64 6 | long_mode_start: 7 | ; load 0 into all data segment registers 8 | mov ax, 0 9 | mov ss, ax 10 | mov ds, ax 11 | mov es, ax 12 | mov fs, ax 13 | mov gs, ax 14 | 15 | ; call the rust main 16 | extern _start 17 | call _start 18 | 19 | ; print `OKAY` to screen 20 | mov rax, 0x2f592f412f4b2f4f 21 | mov qword [0xb8000], rax 22 | hlt 23 | -------------------------------------------------------------------------------- /.cargo/x86_64-kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "arch": "x86_64", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "os": "none", 9 | "executables": true, 10 | "linker-flavor": "ld.lld", 11 | "linker": "rust-lld", 12 | "panic-strategy": "abort", 13 | "disable-redzone": true, 14 | "features": "-mmx,-sse,+soft-float" 15 | } 16 | -------------------------------------------------------------------------------- /amd64/fpu.rs: -------------------------------------------------------------------------------- 1 | pub fn init() { 2 | let mut t: usize; 3 | 4 | unsafe { 5 | asm!("clts", options(nostack)); 6 | asm!("mov cr0, {}", out(reg) t, options(nostack)); 7 | t &= !(1 << 2); 8 | t |= (1 << 1); 9 | asm!("mov {}, cr0", in(reg) t, options(nostack)); 10 | asm!("mov cr4, {}", out(reg) t, options(nostack)); 11 | t |= 3 << 9; 12 | asm!("mov {}, cr4", in(reg) t, options(nostack)); 13 | asm!("fninit", options(nostack)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /amd64/boot/multiboot_header.asm: -------------------------------------------------------------------------------- 1 | section .multiboot_header 2 | header_start: 3 | dd 0xe85250d6 ; magic number (multiboot 2) 4 | dd 0 ; architecture 0 (protected mode i386) 5 | dd header_end - header_start ; header length 6 | ; checksum 7 | dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) 8 | 9 | ; insert optional multiboot tags here 10 | 11 | ; required end tag 12 | dw 0 ; type 13 | dw 0 ; flags 14 | dd 8 ; size 15 | header_end: 16 | -------------------------------------------------------------------------------- /amd64/asm.rs: -------------------------------------------------------------------------------- 1 | /// This module provides I/O Port communication with some inline assembly *magic*. 2 | /// Needs `#![feature(asm)]` (nightly rustc) 3 | 4 | #[inline] 5 | pub unsafe fn read_from_port(port: u16) -> u8 { 6 | let value: u8; 7 | asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack)); 8 | value 9 | } 10 | 11 | #[inline] 12 | pub unsafe fn write_to_port(port: u16, value: u8) { 13 | asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack)); 14 | } 15 | 16 | #[inline] 17 | pub unsafe fn readu16_from_port(port: u16) -> u16 { 18 | let value: u16; 19 | asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack)); 20 | value 21 | } 22 | -------------------------------------------------------------------------------- /amd64/cpuid.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use core::arch::x86_64::__cpuid_count; 3 | 4 | pub fn init() { 5 | let mut brand = String::new(); 6 | 7 | unsafe { 8 | for leaf in 0x80000002..=0x80000004 { 9 | let cpuid = __cpuid_count(leaf, 0); 10 | brand.push_str(&String::from_utf8_lossy(&cpuid.eax.to_le_bytes())); 11 | brand.push_str(&String::from_utf8_lossy(&cpuid.ebx.to_le_bytes())); 12 | brand.push_str(&String::from_utf8_lossy(&cpuid.ecx.to_le_bytes())); 13 | brand.push_str(&String::from_utf8_lossy(&cpuid.edx.to_le_bytes())); 14 | } 15 | } 16 | 17 | crate::log!(brand.as_bytes()); 18 | crate::raw_write!(b"\n"); 19 | } 20 | -------------------------------------------------------------------------------- /amd64/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-kernel" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | # vga = "0.2.6" 8 | # Build fails on latest rustc nightly 9 | vga = { path = "./vga" } 10 | ryu = "1.0" 11 | multiboot2 = "0.10.1" 12 | x86_64 = "0.14.2" 13 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 14 | spin = { version = "0.9.0" } 15 | ps2-mouse = { version = "0.1.3", path = "./ps2-mouse" } 16 | # wee_alloc = { version = "0.4.5", features = ["static_array_backend"] } 17 | # wee_alloc uses an ancient version of spin 18 | static-alloc = "0.2.3" 19 | bit_field = "0.10.1" 20 | # TODO: 21 | # ext2 = { path ="./ext2-rs" } 22 | 23 | [lib] 24 | crate-type = ["staticlib"] 25 | path = "main.rs" 26 | -------------------------------------------------------------------------------- /amd64/mem.rs: -------------------------------------------------------------------------------- 1 | use multiboot2::BootInformation; 2 | 3 | pub fn info(boot_info: &BootInformation) { 4 | if let Some(mmap) = boot_info.memory_map_tag() { 5 | let mut memory_size = 0; 6 | for region in mmap.memory_areas() { 7 | let start_addr = region.start_address(); 8 | let end_addr = region.end_address(); 9 | memory_size += end_addr - start_addr; 10 | let mut buffer = ryu::Buffer::new(); 11 | crate::log!(b"Memory region "); 12 | let start_addr = buffer.format(start_addr as f64); 13 | crate::raw_write!(start_addr.as_bytes()); 14 | crate::raw_write!(b" - "); 15 | let end_addr = buffer.format(end_addr as f64); 16 | crate::raw_write!(end_addr.as_bytes()); 17 | crate::raw_write!(b"\n"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `rkernel` 2 | 3 | A minimal x86_64 Rust OS kernel. 4 | 5 | 6 | https://user-images.githubusercontent.com/34997667/126076271-8ee2c02b-d25a-482d-ba78-32a1c788d5e0.mp4 7 | 8 | - [x] [Multiboot2](https://wiki.osdev.org/Multiboot) 9 | - [x] [VGA driver](https://wiki.osdev.org/VGA_Hardware) 10 | - [x] [PIC](https://wiki.osdev.org/PIC) 11 | - [x] [PIT](https://wiki.osdev.org/Pit) 12 | - [x] [PS/2 Keyboard driver](https://wiki.osdev.org/PS2_Keyboard) 13 | - [x] [PS/2 Mouse driver](https://wiki.osdev.org/PS2_Mouse) 14 | - [x] [TSC](https://wiki.osdev.org/TSC) 15 | - [x] [RTC](https://wiki.osdev.org/RTC) 16 | - [x] [Allocator](https://wiki.osdev.org/Memory_Allocation) 17 | - [x] [ATA PIO](https://wiki.osdev.org/ATA_PIO_Mode) (In progress...) 18 | - [x] [PCI](https://wiki.osdev.org/PCI) 19 | - [x] [PC Speaker](https://wiki.osdev.org/PC_Speaker) 20 | - [ ] Audio driver: Soundblaster 16 21 | - [ ] Network: RTL8139 22 | - [ ] ACPI/APM 23 | - [ ] Testing 24 | - [ ] Run on real hardware 25 | - [ ] (Psuedo)Userspace programs 26 | 27 | -------------------------------------------------------------------------------- /amd64/serial.rs: -------------------------------------------------------------------------------- 1 | use crate::asm::*; 2 | use x86_64::instructions::port::Port; 3 | 4 | pub struct SerialPort(u16); 5 | 6 | impl SerialPort { 7 | pub unsafe fn new(port: u16) -> Self { 8 | let mut p0 = Port::::new(port); 9 | let mut p1 = Port::::new(port + 1); 10 | let mut p2 = Port::::new(port + 2); 11 | let mut p3 = Port::::new(port + 3); 12 | let mut p4 = Port::::new(port + 4); 13 | 14 | // Disable intr 15 | p1.write(0x00); 16 | // Enable divisor mode 17 | p3.write(0x80); 18 | // Set port to 115200 bps 19 | p0.write(0x01); 20 | p1.write(0x00); 21 | // Disable divisor mode 22 | p3.write(0x03); 23 | // Clear FIFO 24 | p2.write(0xC7); 25 | // Enable intr 26 | p4.write(0x0B); 27 | p1.write(0x01); 28 | 29 | Self(port) 30 | } 31 | 32 | pub unsafe fn write(&self, byte: u8) { 33 | while read_from_port(self.0 + 5) & 0x20 == 0 {} 34 | write_to_port(self.0, byte); 35 | } 36 | 37 | pub unsafe fn read(&self) -> u8 { 38 | while read_from_port(self.0 + 5) & 1 == 0 {} 39 | read_from_port(self.0) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /amd64/rtc.rs: -------------------------------------------------------------------------------- 1 | use crate::asm::*; 2 | 3 | fn io_wait() { 4 | unsafe { write_to_port(0, 0x80) }; 5 | } 6 | 7 | unsafe fn read_rtc(address: u8) -> u8 { 8 | write_to_port(0x70, address); // Write to ADDRESS port 9 | io_wait(); 10 | let data = read_from_port(0x71); // Read from DATA port 11 | io_wait(); 12 | data 13 | } 14 | 15 | fn is_updating() -> bool { 16 | unsafe { 17 | write_to_port(0x70, 0x0a); 18 | read_from_port(0x71) & 0x80 == 0 19 | } 20 | } 21 | 22 | fn bcd_to_binary(bcd: u8) -> u8 { 23 | (bcd / 16) * 10 + (bcd & 0xf) 24 | } 25 | 26 | pub fn time() { 27 | while is_updating() {} 28 | 29 | let mode = unsafe { read_rtc(0x0B) }; 30 | let mut s = unsafe { read_rtc(0x00) }; 31 | let mut m = unsafe { read_rtc(0x02) }; 32 | let mut h = unsafe { read_rtc(0x04) }; 33 | let mut d = unsafe { read_rtc(0x07) }; 34 | let mut month = unsafe { read_rtc(0x08) }; 35 | let mut year = unsafe { read_rtc(0x09) }; 36 | 37 | s = bcd_to_binary(s); 38 | m = bcd_to_binary(m); 39 | let is_pm = h & 0x80 == 0; 40 | if is_pm { 41 | h &= 0x7f; 42 | } 43 | h = bcd_to_binary(h); 44 | if is_pm { 45 | h += 12; 46 | } 47 | d = bcd_to_binary(d); 48 | month = bcd_to_binary(month); 49 | year = bcd_to_binary(year); 50 | 51 | let year = 2000 + year as isize; 52 | crate::log!(alloc::format!( 53 | "RTC: year: {}, month: {}, day: {}, hour: {}, minute: {}, second: {} \n", 54 | year, 55 | month, 56 | d, 57 | h, 58 | m, 59 | s 60 | ) 61 | .as_bytes()); 62 | } 63 | -------------------------------------------------------------------------------- /amd64/gdt.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; 3 | use x86_64::structures::tss::TaskStateSegment; 4 | use x86_64::VirtAddr; 5 | 6 | pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; 7 | 8 | lazy_static! { 9 | static ref TSS: TaskStateSegment = { 10 | let mut tss = TaskStateSegment::new(); 11 | tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = { 12 | const STACK_SIZE: usize = 4096 * 5; 13 | static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; 14 | 15 | let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); 16 | let stack_end = stack_start + STACK_SIZE; 17 | stack_end 18 | }; 19 | tss 20 | }; 21 | } 22 | 23 | lazy_static! { 24 | static ref GDT: (GlobalDescriptorTable, Selectors) = { 25 | let mut gdt = GlobalDescriptorTable::new(); 26 | let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); 27 | let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); 28 | ( 29 | gdt, 30 | Selectors { 31 | code_selector, 32 | tss_selector, 33 | }, 34 | ) 35 | }; 36 | } 37 | 38 | struct Selectors { 39 | code_selector: SegmentSelector, 40 | tss_selector: SegmentSelector, 41 | } 42 | 43 | pub fn init() { 44 | use x86_64::instructions::segmentation::set_cs; 45 | use x86_64::instructions::tables::load_tss; 46 | 47 | GDT.0.load(); 48 | unsafe { 49 | set_cs(GDT.1.code_selector); 50 | load_tss(GDT.1.tss_selector); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /amd64/idt.rs: -------------------------------------------------------------------------------- 1 | use crate::pic::Chained; 2 | use lazy_static::lazy_static; 3 | use spin::Mutex; 4 | use x86_64::structures::idt::InterruptDescriptorTable; 5 | use x86_64::structures::idt::InterruptStackFrame; 6 | 7 | pub static PICS: spin::Mutex = spin::Mutex::new(unsafe { Chained::new(32, 32 + 8) }); 8 | 9 | lazy_static! { 10 | static ref IDT: InterruptDescriptorTable = { 11 | let mut idt = InterruptDescriptorTable::new(); 12 | idt.breakpoint.set_handler_fn(breakpoint_handler); 13 | unsafe { 14 | idt.double_fault 15 | .set_handler_fn(double_fault_handler) 16 | .set_stack_index(crate::gdt::DOUBLE_FAULT_IST_INDEX); 17 | } 18 | // Timer 19 | idt[32].set_handler_fn(timer_interrupt_handler); 20 | // PS/2 Keyboard 21 | idt[32 + 1].set_handler_fn(crate::keyboard::keyboard_interrupt_handler); 22 | // Sound card 23 | idt[32 + 5].set_handler_fn(crate::sb16::sound_interrupt_handler); 24 | // PS/2 Mouse 25 | idt[32 + 12].set_handler_fn(crate::mouse::mouse_interrupt_handler); 26 | idt 27 | }; 28 | } 29 | 30 | pub fn init() { 31 | crate::gdt::init(); 32 | IDT.load(); 33 | unsafe { PICS.lock().init() }; 34 | x86_64::instructions::interrupts::enable(); 35 | } 36 | 37 | extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {} 38 | extern "x86-interrupt" fn double_fault_handler( 39 | stack_frame: InterruptStackFrame, 40 | _error_code: u64, 41 | ) -> ! { 42 | panic!(); 43 | } 44 | extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { 45 | unsafe { 46 | PICS.lock().end(32); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /amd64/pspeaker.rs: -------------------------------------------------------------------------------- 1 | use crate::asm::*; 2 | 3 | static NOTES: [[f32; 12]; 7] = [ 4 | [ 5 | 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.0, 196.0, 207.65, 220.0, 227.31, 246.96, 6 | ], 7 | [ 8 | 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.63, 392.0, 415.3, 440.0, 454.62, 493.92, 9 | ], 10 | [ 11 | 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.0, 909.24, 12 | 987.84, 13 | ], 14 | [ 15 | 1046.5, 1108.73, 1174.66, 1244.51, 1328.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.0, 16 | 1818.48, 1975.68, 17 | ], 18 | [ 19 | 2093.0, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.0, 20 | 3636.96, 3951.36, 21 | ], 22 | [ 23 | 4186.0, 4434.92, 4698.64, 4978.04, 5274.04, 5587.86, 5919.92, 6271.92, 6644.88, 7040.0, 24 | 7273.92, 7902.72, 25 | ], 26 | [ 27 | 8372.0, 8869.89, 9397.28, 9956.08, 10548.08, 11175.32, 11839.84, 12543.84, 13289.76, 28 | 14080.0, 14547.84, 15805.44, 29 | ], 30 | ]; 31 | 32 | // TODO: Set PIT back to it's initial frequency 33 | pub unsafe fn play(freq: u32) { 34 | let reload = 1193180 / freq; 35 | write_to_port(0x43, 0xb6); 36 | write_to_port(0x42, reload as u8); 37 | write_to_port(0x42, (reload >> 8) as u8); 38 | 39 | let status = read_from_port(0x61); 40 | if status != status | 3 { 41 | write_to_port(0x61, status | 3); 42 | } 43 | // beep for some time 44 | // TODO: eh there's a better way of waiting 45 | for _ in 0..1000000 {} 46 | let status = read_from_port(0x61) & 0xFC; 47 | write_to_port(0x61, status); 48 | } 49 | 50 | pub fn play_note(octave: usize, note: usize) { 51 | unsafe { play(NOTES[octave][note] as u32) }; 52 | } 53 | -------------------------------------------------------------------------------- /amd64/keyboard.rs: -------------------------------------------------------------------------------- 1 | use crate::asm::*; 2 | use crate::idt::PICS; 3 | use crate::SCREEN; 4 | use x86_64::structures::idt::InterruptStackFrame; 5 | 6 | pub enum Input { 7 | /// A genuine character key. 8 | Key(u8), 9 | Enter, 10 | Esc, 11 | } 12 | 13 | // Based on https://github.com/emk/toyos-rs/blob/c3377fb8c1c92a8c042dd94ad9bfcd9a20470ff9/src/arch/x86_64/keyboard.rs#L114 14 | pub fn scancode_to_input(scancode: u8) -> Option { 15 | let idx = scancode as usize; 16 | match scancode { 17 | 0x01..=0x0D => Some(Input::Key(b"\x1B1234567890-="[idx - 0x01])), 18 | 0x0F..=0x1B => Some(Input::Key(b"\tqwertyuiop[]"[idx - 0x0F])), 19 | 0x1C => Some(Input::Enter), 20 | 0x1E..=0x28 => Some(Input::Key(b"asdfghjkl;'"[idx - 0x1E])), 21 | 0x2C..=0x35 => Some(Input::Key(b"zxcvbnm,./"[idx - 0x2C])), 22 | // TODO(littledivy): Make this unique in keyboard::Input ? 23 | 0x39 => Some(Input::Key(b' ')), 24 | _ => None, 25 | } 26 | } 27 | 28 | pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { 29 | unsafe { 30 | // Get status from command port 31 | // let status = read_from_port(0x64); 32 | 33 | // Data port is busy? 34 | // if status & 0x1 != 0 { 35 | // Get input from data port 36 | let c = read_from_port(0x60); 37 | 38 | // Backspace 39 | if c == 0x0E { 40 | SCREEN.lock().pop(); 41 | } else if let Some(input) = scancode_to_input(c) { 42 | match input { 43 | // Execute command 44 | Input::Enter => { 45 | let cmd = SCREEN.lock().curr_command; 46 | crate::raw_write!(b"[Enter]\n"); 47 | SCREEN.lock().clear_command(); 48 | } 49 | Input::Key(ch) => SCREEN.lock().write_byte(ch), 50 | _ => (), 51 | } 52 | } 53 | //} 54 | 55 | PICS.lock().end(33); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /amd64/mouse.rs: -------------------------------------------------------------------------------- 1 | //! Based on a much readable implementation: https://github.com/dthain/basekernel/blob/master/kernel/mouse.c 2 | 3 | use crate::asm::*; 4 | use crate::idt::PICS; 5 | use crate::SCREEN; 6 | use lazy_static::lazy_static; 7 | use ps2_mouse::Mouse; 8 | use ps2_mouse::MouseState; 9 | use spin::Mutex; 10 | use x86_64::structures::idt::InterruptStackFrame; 11 | 12 | pub struct State(i16, i16); 13 | 14 | lazy_static! { 15 | pub static ref MOUSE: Mutex = Mutex::new(Mouse::new()); 16 | pub static ref MOUSE_STATE: Mutex = Mutex::new(State(0, 0)); 17 | } 18 | 19 | pub fn init() { 20 | MOUSE.lock().init().expect("Mouse init failed."); 21 | MOUSE.lock().set_on_complete(on_complete); 22 | 23 | // Old code. Essentially the same as `ps2_mouse::Mouse::init()` 24 | // unsafe { 25 | // write_to_port(0x64, 0x20); 26 | // let status = read_from_port(0x64) | 0x02; 27 | // write_to_port(0x64, 0x60); 28 | // write_to_port(0x60, status & 0xDF); 29 | // write_to_port(0x64, 0xD4); 30 | // write_to_port(0x60, 0xF6); // Set defaults 31 | // write_to_port(0x64, 0xD4); 32 | // write_to_port(0x60, 0xF4); // Enable packet streaming 33 | // } 34 | } 35 | 36 | fn on_complete(mouse_state: MouseState) { 37 | let mut state = MOUSE_STATE.lock(); 38 | let dx = mouse_state.get_x(); 39 | let dy = mouse_state.get_y(); 40 | state.0 += dx; 41 | state.1 -= dy; 42 | if state.0.is_negative() { 43 | state.0 = 0; 44 | } 45 | if state.1.is_negative() { 46 | state.1 = 0; 47 | } 48 | if state.0 >= 640 { 49 | state.0 = 640; 50 | } 51 | if state.1 >= 480 { 52 | state.1 = 480; 53 | } 54 | SCREEN.lock().restore_pointer(); 55 | SCREEN.lock().set_mouse(state.0 as usize, state.1 as usize); 56 | } 57 | 58 | pub extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: InterruptStackFrame) { 59 | unsafe { 60 | let packet = read_from_port(0x60); 61 | MOUSE.lock().process_packet(packet); 62 | PICS.lock().end(44); 63 | } 64 | } 65 | 66 | // TODO: Is this needed? 67 | pub fn disable_mouse() { 68 | unsafe { 69 | // write_to_port(0x64, 0xD4); 70 | write_to_port(0x64, 0xA7); // Disable mouse 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /amd64/pic.rs: -------------------------------------------------------------------------------- 1 | // Heavily adapted from https://github.com/emk/toyos-rs/blob/master/crates/pic8259_simple/src/lib.rs 2 | 3 | use crate::asm::*; 4 | 5 | pub struct Pic { 6 | offset: u8, 7 | cmd_port: u16, 8 | data_port: u16, 9 | } 10 | 11 | fn iowait() { 12 | unsafe { write_to_port(0x80, 0) }; 13 | } 14 | 15 | impl Pic { 16 | pub const fn new(offset: u8, cmd_port: u16, data_port: u16) -> Self { 17 | Self { 18 | offset, 19 | cmd_port, 20 | data_port, 21 | } 22 | } 23 | 24 | fn end(&self) { 25 | unsafe { 26 | write_to_port(self.cmd_port, 0x20); 27 | } // End of interrupt 28 | } 29 | 30 | fn handle(&self, id: u8) -> bool { 31 | self.offset <= id && id < self.offset + 8 32 | } 33 | 34 | // Read data port 35 | fn read(&self) -> u8 { 36 | unsafe { read_from_port(self.data_port) } 37 | } 38 | } 39 | 40 | // Pair of Pics. 41 | pub struct Chained(Pic, Pic); 42 | 43 | impl Chained { 44 | pub const fn new(o1: u8, o2: u8) -> Self { 45 | Self(Pic::new(o1, 0x20, 0x21), Pic::new(o2, 0xA0, 0xA1)) 46 | } 47 | 48 | pub unsafe fn init(&self) { 49 | let mask0 = self.0.read(); 50 | let mask1 = self.1.read(); 51 | 52 | write_to_port(self.0.cmd_port, 0x11); 53 | iowait(); 54 | write_to_port(self.1.cmd_port, 0x11); 55 | iowait(); 56 | 57 | write_to_port(self.0.data_port, self.0.offset); 58 | iowait(); 59 | write_to_port(self.1.data_port, self.1.offset); 60 | iowait(); 61 | 62 | write_to_port(self.0.data_port, 4); 63 | iowait(); 64 | write_to_port(self.1.data_port, 2); 65 | iowait(); 66 | 67 | write_to_port(self.0.data_port, 0x01); 68 | iowait(); 69 | write_to_port(self.1.data_port, 0x01); 70 | iowait(); 71 | 72 | write_to_port(self.0.data_port, mask0); 73 | write_to_port(self.1.data_port, mask1); 74 | } 75 | 76 | pub fn handle(&self, id: u8) -> bool { 77 | self.0.handle(id) || self.1.handle(id) 78 | } 79 | 80 | pub unsafe fn end(&self, id: u8) { 81 | if self.handle(id) { 82 | if self.1.handle(id) { 83 | self.1.end(); 84 | } 85 | self.0.end(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /amd64/pci.rs: -------------------------------------------------------------------------------- 1 | use spin::Mutex; 2 | use x86_64::instructions::port::Port; 3 | 4 | pub struct Pci { 5 | address_port: Port, 6 | data_port: Port, 7 | } 8 | 9 | static PCI: Mutex = Mutex::new(unsafe { Pci::default() }); 10 | 11 | impl Pci { 12 | pub unsafe fn config_read(&mut self, bus: u8, slot: u8, function: u8, offset: u8) -> u32 { 13 | self.address_port.write( 14 | 0x80000000 15 | | (bus as u32) << 16 16 | | (slot as u32) << 11 17 | | (function as u32) << 8 18 | | (offset & 0b1111_1100) as u32, 19 | ); 20 | 21 | self.data_port.read() 22 | } 23 | 24 | pub const unsafe fn default() -> Self { 25 | Self { 26 | address_port: Port::new(0xCF8), 27 | data_port: Port::new(0xCFC), 28 | } 29 | } 30 | } 31 | 32 | pub struct PciDevice { 33 | vendor_id: u32, 34 | device_id: u32, 35 | subclass: u32, 36 | class: u32, 37 | header_type: u32, 38 | } 39 | 40 | impl PciDevice { 41 | pub unsafe fn new(bus: u8, slot: u8, function: u8) -> Self { 42 | let mut pci = PCI.lock(); 43 | Self { 44 | vendor_id: pci.config_read(bus, slot, function, 0x0), 45 | device_id: pci.config_read(bus, slot, function, 0x2), 46 | subclass: pci.config_read(bus, slot, function, 0xa), 47 | class: pci.config_read(bus, slot, function, 0xb), 48 | header_type: pci.config_read(bus, slot, function, 0xe), 49 | } 50 | } 51 | } 52 | 53 | fn device_class() {} 54 | pub fn init() { 55 | for slot in 0..31 { 56 | unsafe { 57 | let device = PciDevice::new(0, slot, 0); 58 | if device.vendor_id != 0xFFFFFFFF { 59 | for function in 0..7 { 60 | let device = PciDevice::new(0, slot, function); 61 | if device.vendor_id != 0xFFFFFFFF { 62 | let info = alloc::format!( 63 | "PCI (0:{}:{}) => {:04x} {:04x}\n", 64 | slot, 65 | function, 66 | device.vendor_id, 67 | device.device_id 68 | ); 69 | crate::log!(info.as_bytes()); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /amd64/ata.rs: -------------------------------------------------------------------------------- 1 | use crate::asm::*; 2 | use bit_field::BitField; 3 | 4 | fn wait_bsy() { 5 | while unsafe { read_from_port(0x80) } & 0x80 == 0 {} 6 | } 7 | 8 | fn wait_drq() { 9 | while unsafe { read_from_port(0x08) } & 0x40 != 0 {} 10 | } 11 | 12 | pub unsafe fn read(lba: u32, sector: u8, buf: &mut [u8]) { 13 | wait_bsy(); 14 | 15 | write_to_port(0x1F6, 0xE0 | ((lba >> 24) & 0xF) as u8); 16 | write_to_port(0x1F2, sector); 17 | write_to_port(0x1F3, lba as u8); 18 | write_to_port(0x1F4, (lba >> 8) as u8); 19 | write_to_port(0x1F5, (lba >> 16) as u8); 20 | 21 | // READ Command 22 | write_to_port(0x1F7, 0x20); 23 | 24 | for i in 0..sector { 25 | wait_bsy(); 26 | wait_drq(); 27 | 28 | for j in 0..256 { 29 | let data = readu16_from_port(0x1F0); 30 | buf[j * 2] = data.get_bits(0..8) as u8; 31 | buf[j * 2 + 1] = data.get_bits(8..16) as u8; 32 | } 33 | } 34 | } 35 | 36 | pub unsafe fn prune(drive: u8) { 37 | write_to_port(0x1F0 + 6, 0xA0 | (drive << 4)); // Select drive 38 | 39 | write_to_port(0x1F0 + 7, 0xEC); // IDENTIFY Command 40 | 41 | if read_from_port(0x1F0 + 7) != 0 { 42 | if read_from_port(0x1F0 + 3) == 0 || read_from_port(0x1F0 + 4) == 0 { 43 | // Check LB1 and LB2 44 | for i in 0..256 { 45 | if read_from_port(0x1F0 + 7).get_bit(6) { 46 | let mut res = [0; 256]; 47 | for i in 0..256 { 48 | let data = readu16_from_port(0x1F0); 49 | res[i] = data; 50 | } 51 | 52 | let mut model = alloc::string::String::new(); 53 | for &r in res.get(27..47).unwrap() { 54 | for &b in r.to_be_bytes().iter() { 55 | model.push(b as char); 56 | } 57 | } 58 | let bytes = ((res[61] as u32) << 16 | (res[60] as u32)) * 512; 59 | 60 | crate::log!( 61 | alloc::format!("ATA drive: {} ({} bytes)\n", model.trim(), bytes) 62 | .as_bytes() 63 | ); 64 | break; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | // TODO: Anti-panic 72 | pub fn init() { 73 | for d in 0..2 { 74 | unsafe { 75 | prune(d); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /amd64/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm)] 3 | #![feature(abi_x86_interrupt)] 4 | #![feature(alloc_error_handler)] 5 | #![feature(core_intrinsics)] 6 | #![feature(panic_info_message)] 7 | 8 | extern crate alloc; 9 | 10 | use alloc::boxed::Box; 11 | use static_alloc::Bump; 12 | 13 | // Use `static_alloc` as the global allocator. 14 | #[global_allocator] 15 | static ALLOC: Bump<[u8; 33554432]> = Bump::uninit(); 16 | 17 | use core::panic::PanicInfo; 18 | use lazy_static::lazy_static; 19 | use multiboot2::load; 20 | use spin::Mutex; 21 | use vga::colors::Color16; 22 | 23 | mod asm; 24 | mod ata; 25 | mod cpuid; 26 | mod fpu; 27 | mod gdt; 28 | mod graphics; 29 | mod idt; 30 | mod keyboard; 31 | mod mem; 32 | mod mouse; 33 | mod pci; 34 | mod pic; 35 | mod pspeaker; 36 | mod rdtsc; 37 | mod rtc; 38 | mod sb16; 39 | mod serial; 40 | 41 | use asm::*; 42 | use graphics::Screen; 43 | use keyboard::scancode_to_input; 44 | use keyboard::Input; 45 | use mouse::disable_mouse; 46 | use rdtsc::rdtsc; 47 | 48 | lazy_static! { 49 | pub static ref SCREEN: Mutex = Mutex::new(Screen::new()); 50 | } 51 | 52 | /// TSC timestamp at boot time stored here. 53 | static mut BOOT_TICKS: f64 = 0.0; 54 | 55 | // TODO: Implement `cowsay` once we have a userspace / command centre 56 | static WELCOME: &[u8] = br#" ____________________ 57 | < rkernel is da best > 58 | -------------------- 59 | \ ^__^ 60 | \ (oo)\_______ 61 | (__)\ )\/\ 62 | ||----w | 63 | || || 64 | "#; 65 | 66 | // TODO: Maybe rename to print 67 | #[macro_export] 68 | macro_rules! raw_write { 69 | ($m: expr) => { 70 | crate::SCREEN.lock().write($m, vga::colors::Color16::White); 71 | }; 72 | } 73 | 74 | #[macro_export] 75 | macro_rules! log { 76 | ($msg: expr) => { 77 | crate::raw_write!(b"["); 78 | #[allow(unused_unsafe)] 79 | let boot_t = unsafe { crate::BOOT_TICKS }; 80 | let dt = crate::rdtsc::delta_ns(boot_t); 81 | let mut buffer = ryu::Buffer::new(); 82 | let printable = buffer.format(dt); 83 | crate::SCREEN 84 | .lock() 85 | .write(&printable.as_bytes(), vga::colors::Color16::LightGreen); 86 | crate::SCREEN 87 | .lock() 88 | .write(b"ns", vga::colors::Color16::LightGreen); 89 | crate::raw_write!(b"] "); 90 | crate::raw_write!($msg); 91 | }; 92 | } 93 | 94 | #[no_mangle] 95 | pub extern "C" fn _start(m_ptr: usize) -> ! { 96 | unsafe { BOOT_TICKS = rdtsc() }; 97 | let boot_info = unsafe { load(m_ptr) }; 98 | log!(b"Enter _start\n"); 99 | log!(b"TSC calibrated\n"); 100 | mem::info(&boot_info); 101 | cpuid::init(); 102 | mouse::init(); 103 | log!(b"Mouse enabled\n"); 104 | ata::init(); 105 | pci::init(); 106 | // FIXME: FPU initalization sometimes crashes. 107 | // fpu::init(); 108 | idt::init(); 109 | rtc::time(); 110 | log!(b"Interrupts enabled\n"); 111 | 112 | // Play the 3rd Octave :D 113 | // for note in 0..7 { 114 | // pspeaker::play_note(3, note); 115 | // } 116 | 117 | raw_write!(WELCOME); 118 | 119 | let (v_major, v_minor) = sb16::init(); 120 | log!(alloc::format!("SB16: {} {}\n", v_major, v_minor).as_bytes()); 121 | 122 | // TODO: use `hlt` instruction 123 | loop {} 124 | } 125 | 126 | #[panic_handler] 127 | fn panic(info: &PanicInfo) -> ! { 128 | raw_write!(alloc::format!("{}", info).as_bytes()); 129 | // TODO: ACPI/APM shutdown or QEMU exit after few seconds. 130 | loop {} 131 | } 132 | 133 | #[alloc_error_handler] 134 | #[no_mangle] 135 | #[allow(improper_ctypes_definitions)] 136 | pub extern "C" fn oom(_: ::core::alloc::Layout) -> ! { 137 | raw_write!(b"OOM"); 138 | unsafe { 139 | ::core::intrinsics::abort(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /amd64/sb16.rs: -------------------------------------------------------------------------------- 1 | use crate::asm::*; 2 | use crate::idt::PICS; 3 | use lazy_static::lazy_static; 4 | use spin::Mutex; 5 | use x86_64::instructions::port::Port; 6 | use x86_64::instructions::port::PortRead; 7 | use x86_64::instructions::port::PortReadOnly; 8 | use x86_64::structures::idt::InterruptStackFrame; 9 | 10 | const SB_RESET: u16 = 0x226; 11 | const SB_READ: u16 = 0x22A; 12 | const SB_READ_STATUS: u16 = 0x22E; 13 | const SB_WRITE: u16 = 0x22C; 14 | 15 | lazy_static! { 16 | pub static ref SB16: Mutex = Mutex::new(SoundBlaster::new()); 17 | } 18 | 19 | static mut BUF: &[u8; 882] = &[55; 882]; 20 | 21 | pub fn init() -> (u8, u8) { 22 | unsafe { 23 | SB16.lock().reset(); 24 | SB16.lock().init() 25 | } 26 | } 27 | 28 | pub struct SoundBlaster { 29 | read_port: Port, 30 | write_port: Port, 31 | reset_port: Port, 32 | read_status_port: PortReadOnly, 33 | } 34 | 35 | impl SoundBlaster { 36 | pub fn new() -> Self { 37 | Self { 38 | read_port: Port::new(SB_READ), 39 | write_port: Port::new(SB_WRITE), 40 | reset_port: Port::new(SB_READ), 41 | read_status_port: PortReadOnly::new(SB_READ_STATUS), 42 | } 43 | } 44 | 45 | pub unsafe fn init(&mut self) -> (u8, u8) { 46 | self.write_port.read(); 47 | self.write_port.write(0xE1); 48 | 49 | // (Version Major, Version Minor) 50 | let version = (self.read_port.read(), self.read_port.read()); 51 | 52 | self.set_sample_rate(22050); 53 | self._write(0xb0); 54 | self._write(0x10 | 0x00); 55 | 56 | let buffer_size = 22050 * (40 / 1000); 57 | let sample_count = (buffer_size / 2) - 1; 58 | 59 | self._write(((sample_count >> 0) & 0xFF) as u8); 60 | self._write(((sample_count >> 8) & 0xFF) as u8); 61 | 62 | self._write(0xd1); 63 | self._write(0xd6); 64 | 65 | write_to_port(0x224, 0x80); 66 | write_to_port(0x225, 0b10); 67 | // self.transfer(BUF as *const u8, 882); 68 | version 69 | } 70 | 71 | pub unsafe fn reset(&mut self) -> bool { 72 | self.reset_port.write(1); 73 | self.reset_port.write(0); 74 | 75 | self.read_port.read() == 0xAA 76 | } 77 | 78 | unsafe fn _write(&mut self, byte: u8) { 79 | while self.write_port.read() & 0x80 != 0 {} 80 | self.write_port.write(byte); 81 | } 82 | 83 | unsafe fn _read(&mut self, byte: u8) { 84 | while self.read_status_port.read() & 0x80 == 0 {} 85 | self.read_port.write(byte); 86 | } 87 | 88 | pub unsafe fn set_sample_rate(&mut self, freq: u16) { 89 | self._write(0x41); 90 | self._write(((freq >> 8) & 0xFF) as u8); 91 | self._write((freq & 0xFF) as u8); 92 | } 93 | 94 | pub unsafe fn transfer(&mut self, buf: *const u8, len: u32) { 95 | let mode = 0x48; 96 | let channel = 5; 97 | 98 | write_to_port(0xd4, 4 + (channel % 4)); 99 | write_to_port(0xd8, 1); 100 | write_to_port(0xd6, (channel % 4) | mode | (1 << 4)); 101 | 102 | let offset: u16 = ((buf as usize / 2) % 65536) as u16; 103 | 104 | write_to_port(0xc4, (offset >> 0 & 0xFF) as u8); 105 | write_to_port(0xc4, (offset >> 8 & 0xFF) as u8); 106 | 107 | write_to_port(0xc6, ((len - 1) & 0xFF) as u8); 108 | write_to_port(0xc6, (((len - 1) >> 8) & 0xFF) as u8); 109 | 110 | write_to_port(0x8b, (buf as usize >> 16) as u8); 111 | 112 | write_to_port(0xd4, channel % 4); 113 | } 114 | } 115 | 116 | pub extern "x86-interrupt" fn sound_interrupt_handler(_stack_frame: InterruptStackFrame) { 117 | unsafe { 118 | SB16.lock()._write(0xd5); 119 | read_from_port(0x22F); 120 | PICS.lock().end(32 + 5); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /amd64/boot/boot.asm: -------------------------------------------------------------------------------- 1 | 2 | global start 3 | extern long_mode_start 4 | 5 | section .text 6 | bits 32 7 | start: 8 | mov esp, stack_top 9 | mov edi, ebx ; Move Multiboot info pointer to edi 10 | 11 | call check_multiboot 12 | call check_cpuid 13 | call check_long_mode 14 | 15 | call set_up_page_tables 16 | call enable_paging 17 | 18 | ; load the 64-bit GDT 19 | lgdt [gdt64.pointer] 20 | 21 | jmp gdt64.code:long_mode_start 22 | 23 | ; print `OK` to screen 24 | mov dword [0xb8000], 0x2f4b2f4f 25 | hlt 26 | 27 | check_multiboot: 28 | cmp eax, 0x36d76289 29 | jne .no_multiboot 30 | ret 31 | .no_multiboot: 32 | mov al, "0" 33 | jmp error 34 | 35 | check_cpuid: 36 | ; Check if CPUID is supported by attempting to flip the ID bit (bit 21) 37 | ; in the FLAGS register. If we can flip it, CPUID is available. 38 | 39 | ; Copy FLAGS in to EAX via stack 40 | pushfd 41 | pop eax 42 | 43 | ; Copy to ECX as well for comparing later on 44 | mov ecx, eax 45 | 46 | ; Flip the ID bit 47 | xor eax, 1 << 21 48 | 49 | ; Copy EAX to FLAGS via the stack 50 | push eax 51 | popfd 52 | 53 | ; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) 54 | pushfd 55 | pop eax 56 | 57 | ; Restore FLAGS from the old version stored in ECX (i.e. flipping the 58 | ; ID bit back if it was ever flipped). 59 | push ecx 60 | popfd 61 | 62 | ; Compare EAX and ECX. If they are equal then that means the bit 63 | ; wasn't flipped, and CPUID isn't supported. 64 | cmp eax, ecx 65 | je .no_cpuid 66 | ret 67 | .no_cpuid: 68 | mov al, "1" 69 | jmp error 70 | 71 | check_long_mode: 72 | ; test if extended processor info in available 73 | mov eax, 0x80000000 ; implicit argument for cpuid 74 | cpuid ; get highest supported argument 75 | cmp eax, 0x80000001 ; it needs to be at least 0x80000001 76 | jb .no_long_mode ; if it's less, the CPU is too old for long mode 77 | 78 | ; use extended info to test if long mode is available 79 | mov eax, 0x80000001 ; argument for extended processor info 80 | cpuid ; returns various feature bits in ecx and edx 81 | test edx, 1 << 29 ; test if the LM-bit is set in the D-register 82 | jz .no_long_mode ; If it's not set, there is no long mode 83 | ret 84 | .no_long_mode: 85 | mov al, "2" 86 | jmp error 87 | 88 | set_up_page_tables: 89 | ; map first P4 entry to P3 table 90 | mov eax, p3_table 91 | or eax, 0b11 ; present + writable 92 | mov [p4_table], eax 93 | 94 | ; map first P3 entry to P2 table 95 | mov eax, p2_table 96 | or eax, 0b11 ; present + writable 97 | mov [p3_table], eax 98 | 99 | ; map each P2 entry to a huge 2MiB page 100 | mov ecx, 0 ; counter variable 101 | 102 | .map_p2_table: 103 | ; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx 104 | mov eax, 0x200000 ; 2MiB 105 | mul ecx ; start address of ecx-th page 106 | or eax, 0b10000011 ; present + writable + huge 107 | mov [p2_table + ecx * 8], eax ; map ecx-th entry 108 | 109 | inc ecx ; increase counter 110 | cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped 111 | jne .map_p2_table ; else map the next entry 112 | 113 | ret 114 | 115 | enable_paging: 116 | ; load P4 to cr3 register (cpu uses this to access the P4 table) 117 | mov eax, p4_table 118 | mov cr3, eax 119 | 120 | ; enable PAE-flag in cr4 (Physical Address Extension) 121 | mov eax, cr4 122 | or eax, 1 << 5 123 | mov cr4, eax 124 | 125 | ; set the long mode bit in the EFER MSR (model specific register) 126 | mov ecx, 0xC0000080 127 | rdmsr 128 | or eax, 1 << 8 129 | wrmsr 130 | 131 | ; enable paging in the cr0 register 132 | mov eax, cr0 133 | or eax, 1 << 31 134 | mov cr0, eax 135 | 136 | ret 137 | 138 | ; Prints `ERR: ` and the given error code to screen and hangs. 139 | ; parameter: error code (in ascii) in al 140 | error: 141 | mov dword [0xb8000], 0x4f524f45 142 | mov dword [0xb8004], 0x4f3a4f52 143 | mov dword [0xb8008], 0x4f204f20 144 | mov byte [0xb800a], al 145 | hlt 146 | 147 | section .bss 148 | align 4096 149 | p4_table: 150 | resb 4096 151 | p3_table: 152 | resb 4096 153 | p2_table: 154 | resb 4096 155 | stack_bottom: 156 | resb 80000 157 | stack_top: 158 | 159 | section .rodata 160 | gdt64: 161 | dq 0 ; zero entry 162 | .code: equ $ - gdt64 ; new 163 | dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment 164 | .pointer: 165 | dw $ - gdt64 - 1 166 | dq gdt64 167 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "alloc-traits" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" 10 | 11 | [[package]] 12 | name = "autocfg" 13 | version = "1.0.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 16 | 17 | [[package]] 18 | name = "bit_field" 19 | version = "0.9.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" 22 | 23 | [[package]] 24 | name = "bit_field" 25 | version = "0.10.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "1.2.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 34 | 35 | [[package]] 36 | name = "conquer-once" 37 | version = "0.3.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "7c6d3a9775a69f6d1fe2cc888999b67ed30257d3da4d2af91984e722f2ec918a" 40 | dependencies = [ 41 | "conquer-util", 42 | ] 43 | 44 | [[package]] 45 | name = "conquer-util" 46 | version = "0.3.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582" 49 | 50 | [[package]] 51 | name = "font8x8" 52 | version = "0.3.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e" 55 | 56 | [[package]] 57 | name = "lazy_static" 58 | version = "1.4.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 61 | dependencies = [ 62 | "spin 0.5.2", 63 | ] 64 | 65 | [[package]] 66 | name = "lock_api" 67 | version = "0.4.4" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 70 | dependencies = [ 71 | "scopeguard", 72 | ] 73 | 74 | [[package]] 75 | name = "multiboot2" 76 | version = "0.10.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "13e9fc890ff2ef2ded9084c964b950da1bc9e26d480e446ae721607b899db1fd" 79 | dependencies = [ 80 | "bitflags", 81 | ] 82 | 83 | [[package]] 84 | name = "num-traits" 85 | version = "0.2.14" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 88 | dependencies = [ 89 | "autocfg", 90 | ] 91 | 92 | [[package]] 93 | name = "ps2-mouse" 94 | version = "0.1.3" 95 | dependencies = [ 96 | "bitflags", 97 | "x86_64", 98 | ] 99 | 100 | [[package]] 101 | name = "rust-kernel" 102 | version = "0.1.0" 103 | dependencies = [ 104 | "bit_field 0.10.1", 105 | "lazy_static", 106 | "multiboot2", 107 | "ps2-mouse", 108 | "ryu", 109 | "spin 0.9.0", 110 | "static-alloc", 111 | "vga", 112 | "x86_64", 113 | ] 114 | 115 | [[package]] 116 | name = "ryu" 117 | version = "1.0.5" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 120 | 121 | [[package]] 122 | name = "scopeguard" 123 | version = "1.1.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 126 | 127 | [[package]] 128 | name = "spin" 129 | version = "0.5.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 132 | 133 | [[package]] 134 | name = "spin" 135 | version = "0.9.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "b87bbf98cb81332a56c1ee8929845836f85e8ddd693157c30d76660196014478" 138 | dependencies = [ 139 | "lock_api", 140 | ] 141 | 142 | [[package]] 143 | name = "spinning_top" 144 | version = "0.2.4" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" 147 | dependencies = [ 148 | "lock_api", 149 | ] 150 | 151 | [[package]] 152 | name = "static-alloc" 153 | version = "0.2.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "7ba7261e1a48f13244bf2bdfcbddf87567570749056c8747d23b2afa485b8e35" 156 | dependencies = [ 157 | "alloc-traits", 158 | ] 159 | 160 | [[package]] 161 | name = "vga" 162 | version = "0.2.6" 163 | dependencies = [ 164 | "bitflags", 165 | "conquer-once", 166 | "font8x8", 167 | "num-traits", 168 | "spinning_top", 169 | "x86_64", 170 | ] 171 | 172 | [[package]] 173 | name = "volatile" 174 | version = "0.4.4" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0" 177 | 178 | [[package]] 179 | name = "x86_64" 180 | version = "0.14.3" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "b7c54a17492391c594753ce2a180142bec7ad2876543565c2a08aa11cddef251" 183 | dependencies = [ 184 | "bit_field 0.9.0", 185 | "bitflags", 186 | "volatile", 187 | ] 188 | -------------------------------------------------------------------------------- /amd64/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "alloc-traits" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" 10 | 11 | [[package]] 12 | name = "autocfg" 13 | version = "1.0.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 16 | 17 | [[package]] 18 | name = "bit_field" 19 | version = "0.9.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" 22 | 23 | [[package]] 24 | name = "bit_field" 25 | version = "0.10.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "1.2.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 34 | 35 | [[package]] 36 | name = "conquer-once" 37 | version = "0.3.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "7c6d3a9775a69f6d1fe2cc888999b67ed30257d3da4d2af91984e722f2ec918a" 40 | dependencies = [ 41 | "conquer-util", 42 | ] 43 | 44 | [[package]] 45 | name = "conquer-util" 46 | version = "0.3.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582" 49 | 50 | [[package]] 51 | name = "font8x8" 52 | version = "0.3.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e" 55 | 56 | [[package]] 57 | name = "lazy_static" 58 | version = "1.4.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 61 | dependencies = [ 62 | "spin 0.5.2", 63 | ] 64 | 65 | [[package]] 66 | name = "lock_api" 67 | version = "0.4.4" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 70 | dependencies = [ 71 | "scopeguard", 72 | ] 73 | 74 | [[package]] 75 | name = "multiboot2" 76 | version = "0.10.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "13e9fc890ff2ef2ded9084c964b950da1bc9e26d480e446ae721607b899db1fd" 79 | dependencies = [ 80 | "bitflags", 81 | ] 82 | 83 | [[package]] 84 | name = "num-traits" 85 | version = "0.2.14" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 88 | dependencies = [ 89 | "autocfg", 90 | ] 91 | 92 | [[package]] 93 | name = "ps2-mouse" 94 | version = "0.1.3" 95 | dependencies = [ 96 | "bitflags", 97 | "x86_64", 98 | ] 99 | 100 | [[package]] 101 | name = "rust-kernel" 102 | version = "0.1.0" 103 | dependencies = [ 104 | "bit_field 0.10.1", 105 | "lazy_static", 106 | "multiboot2", 107 | "ps2-mouse", 108 | "ryu", 109 | "spin 0.9.0", 110 | "static-alloc", 111 | "vga", 112 | "x86_64", 113 | ] 114 | 115 | [[package]] 116 | name = "ryu" 117 | version = "1.0.5" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 120 | 121 | [[package]] 122 | name = "scopeguard" 123 | version = "1.1.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 126 | 127 | [[package]] 128 | name = "spin" 129 | version = "0.5.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 132 | 133 | [[package]] 134 | name = "spin" 135 | version = "0.9.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "b87bbf98cb81332a56c1ee8929845836f85e8ddd693157c30d76660196014478" 138 | dependencies = [ 139 | "lock_api", 140 | ] 141 | 142 | [[package]] 143 | name = "spinning_top" 144 | version = "0.2.4" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" 147 | dependencies = [ 148 | "lock_api", 149 | ] 150 | 151 | [[package]] 152 | name = "static-alloc" 153 | version = "0.2.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "7ba7261e1a48f13244bf2bdfcbddf87567570749056c8747d23b2afa485b8e35" 156 | dependencies = [ 157 | "alloc-traits", 158 | ] 159 | 160 | [[package]] 161 | name = "vga" 162 | version = "0.2.6" 163 | dependencies = [ 164 | "bitflags", 165 | "conquer-once", 166 | "font8x8", 167 | "num-traits", 168 | "spinning_top", 169 | "x86_64", 170 | ] 171 | 172 | [[package]] 173 | name = "volatile" 174 | version = "0.4.4" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0" 177 | 178 | [[package]] 179 | name = "x86_64" 180 | version = "0.14.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "13f09cffc08ee86bf5e4d147f107a43de0885c53ffad799b39f4ad203fb2a27d" 183 | dependencies = [ 184 | "bit_field 0.9.0", 185 | "bitflags", 186 | "volatile", 187 | ] 188 | -------------------------------------------------------------------------------- /amd64/graphics.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | use vga::colors::Color16; 4 | use vga::writers::Graphics640x480x16; 5 | use vga::writers::GraphicsWriter; 6 | 7 | trait Writer { 8 | fn inc(&mut self) {} 9 | fn dec(&mut self) {} 10 | } 11 | 12 | struct CommandWriter { 13 | pub x: usize, 14 | pub y: usize, 15 | } 16 | 17 | static COMMAND_Y: usize = 480 - 16; 18 | 19 | pub static FONT: &'static [u8] = include_bytes!("unifont.font"); 20 | 21 | impl CommandWriter { 22 | fn init() -> Self { 23 | Self { 24 | x: 16, 25 | y: COMMAND_Y, 26 | } 27 | } 28 | } 29 | 30 | impl Writer for CommandWriter { 31 | fn inc(&mut self) { 32 | self.x += 8; 33 | if self.x > 640 { 34 | self.x = 8; 35 | } 36 | } 37 | 38 | fn dec(&mut self) { 39 | if self.x <= 8 { 40 | self.x = 8; 41 | } else { 42 | self.x -= 8; 43 | } 44 | } 45 | } 46 | 47 | struct StageWriter { 48 | pub x: usize, 49 | pub y: usize, 50 | } 51 | 52 | impl StageWriter { 53 | pub fn init() -> Self { 54 | Self { x: 0, y: 0 } 55 | } 56 | } 57 | 58 | impl Writer for StageWriter { 59 | fn inc(&mut self) { 60 | self.x += 8; 61 | if self.x >= 640 { 62 | self.x = 0; 63 | self.y += 16; 64 | } 65 | } 66 | 67 | fn dec(&mut self) { 68 | if self.x == 0 { 69 | self.x = 0; 70 | if self.y < 1 { 71 | self.y = 1; 72 | } else { 73 | self.y -= 16; 74 | } 75 | } else { 76 | self.x -= 16; 77 | } 78 | } 79 | } 80 | 81 | struct Point { 82 | x: usize, 83 | y: usize, 84 | } 85 | 86 | struct MousePointer { 87 | loc: Point, 88 | preserved: Option, 89 | } 90 | 91 | impl Default for MousePointer { 92 | fn default() -> Self { 93 | Self { 94 | loc: Point { x: 0, y: 0 }, 95 | preserved: None, 96 | } 97 | } 98 | } 99 | 100 | pub struct Screen { 101 | pub mode: Graphics640x480x16, 102 | cmd: CommandWriter, 103 | pub curr_command: [u8; 310], 104 | stage: StageWriter, 105 | pointer: MousePointer, 106 | } 107 | 108 | impl Screen { 109 | pub fn new() -> Self { 110 | let mode = Graphics640x480x16::new(); 111 | mode.set_mode(); 112 | mode.clear_screen(Color16::Black); 113 | 114 | let mut screen = Self { 115 | mode, 116 | cmd: CommandWriter::init(), 117 | stage: StageWriter::init(), 118 | pointer: Default::default(), 119 | curr_command: [0u8; 310], 120 | }; 121 | screen.draw_character(0, COMMAND_Y, '~', Color16::Pink); 122 | screen 123 | } 124 | 125 | pub fn write(&mut self, buf: &[u8], fg: Color16) { 126 | for (offset, ch) in buf.iter().enumerate() { 127 | if ch == &0u8 { 128 | continue; 129 | }; 130 | if self.stage.y >= COMMAND_Y - 16 { 131 | // ;) 132 | *self = Screen::new(); 133 | } 134 | if ch == &b'\n' { 135 | self.stage.x = 0; 136 | self.stage.y += 16; 137 | } else { 138 | self.draw_character(self.stage.x, self.stage.y, *ch as char, fg); 139 | self.stage.inc(); 140 | } 141 | } 142 | } 143 | 144 | pub fn draw_character(&mut self, x: usize, y: usize, ch: char, color: Color16) { 145 | let font_i = 16 * (ch as usize); 146 | if font_i + 16 <= FONT.len() { 147 | for row in 0..16 { 148 | let row_data = FONT[font_i + row]; 149 | for col in 0..8 { 150 | if (row_data >> (7 - col)) & 1 == 1 { 151 | self.mode.set_pixel(x + col, y + row, color); 152 | } 153 | } 154 | } 155 | } 156 | } 157 | 158 | pub fn write_byte(&mut self, ch: u8) { 159 | self.draw_character(self.cmd.x, self.cmd.y, ch as char, Color16::White); 160 | self.curr_command[self.cmd.x] = ch; 161 | self.cmd.inc(); 162 | } 163 | 164 | pub fn clear_command(&mut self) { 165 | while self.cmd.x != 8 { 166 | self.pop(); 167 | } 168 | self.pop(); 169 | self.curr_command = [0u8; 310]; 170 | } 171 | 172 | pub fn pop(&mut self) { 173 | self.cmd.dec(); 174 | for row in 0..16 { 175 | for col in 0..8 { 176 | self.mode 177 | .set_pixel(self.cmd.x + col, self.cmd.y + row, Color16::Black); 178 | } 179 | } 180 | } 181 | 182 | pub fn set_mouse(&mut self, x: usize, y: usize) { 183 | let framebuffer = self.mode.get_frame_buffer(); 184 | 185 | let offset = x / 8 + y * (640 / 8); 186 | let pixel_mask = 0x80 >> (x & 0x07); 187 | vga::vga::VGA 188 | .lock() 189 | .graphics_controller_registers 190 | .set_bit_mask(pixel_mask); 191 | 192 | let pixel = unsafe { framebuffer.add(offset).read_volatile() }; 193 | if pixel != 0x0 { 194 | return; 195 | } 196 | self.pointer.loc.x = x; 197 | self.pointer.loc.y = y; 198 | 199 | self.pointer.preserved = Some(Color16::Black); 200 | self.mode.set_pixel(x, y, Color16::LightRed); 201 | } 202 | 203 | pub fn restore_pointer(&mut self) { 204 | if let Some(character) = self.pointer.preserved { 205 | self.mode 206 | .set_pixel(self.pointer.loc.x, self.pointer.loc.y, character); 207 | } 208 | } 209 | } 210 | --------------------------------------------------------------------------------