├── Xargo.toml ├── memory ├── READEME ├── Cargo.toml ├── LICENSE └── src │ ├── frame.rs │ ├── paging │ ├── entry.rs │ ├── temporary_page.rs │ ├── mapper.rs │ ├── table.rs │ └── mod.rs │ ├── heap_allocator.rs │ ├── stack_allocator.rs │ ├── lib.rs │ └── area_frame_allocator.rs ├── vga ├── Cargo.toml └── src │ └── lib.rs ├── src ├── arch │ └── x86_64 │ │ ├── grub.cfg │ │ ├── long_mode_init.asm │ │ ├── multiboot_header.asm │ │ ├── linker.ld │ │ └── boot.asm └── kernel.rs ├── device ├── src │ ├── disk.rs │ ├── lib.rs │ ├── io.rs │ ├── tty.rs │ ├── pic.rs │ ├── keyboard.rs │ └── ata.rs └── Cargo.toml ├── interrupt ├── Cargo.toml └── src │ ├── dtables.rs │ ├── lib.rs │ ├── idt.rs │ └── macros.rs ├── filesystem ├── Cargo.toml └── src │ ├── fat32 │ ├── bpb.rs │ ├── cluster.rs │ ├── directory.rs │ └── mod.rs │ ├── file.rs │ └── lib.rs ├── .gitignore ├── x86_64-kurumi.json ├── Cargo.toml ├── makefat32.sh ├── LICENSE ├── README.md └── Makefile /Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-kurumi.dependencies] 2 | alloc = {} 3 | -------------------------------------------------------------------------------- /memory/READEME: -------------------------------------------------------------------------------- 1 | Some code was from http://os.phil-opp.com/allocating-frames.html licensed under the MIT License. 2 | -------------------------------------------------------------------------------- /vga/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vga" 3 | version = "0.1.0" 4 | authors = ["Hanaasagi "] 5 | 6 | [dependencies] 7 | spin = "0.4.6" 8 | -------------------------------------------------------------------------------- /src/arch/x86_64/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "kurumi" { 5 | multiboot2 /boot/kernel.bin 6 | echo 'kurumi is booting ...' 7 | boot 8 | } 9 | -------------------------------------------------------------------------------- /device/src/disk.rs: -------------------------------------------------------------------------------- 1 | pub trait Disk { 2 | unsafe fn read(&self, block: u64, buffer: &mut [u8]) -> Result; 3 | unsafe fn write_at(&self, block: u64, buffer: &[u8]) -> Result; 4 | } 5 | -------------------------------------------------------------------------------- /device/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "device" 3 | version = "0.1.0" 4 | authors = ["Hanaasagi "] 5 | 6 | [dependencies] 7 | bitflags = "1" 8 | spin = "0.4.6" 9 | 10 | [dependencies.vga] 11 | path = "../vga" 12 | -------------------------------------------------------------------------------- /interrupt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interrupt" 3 | version = "0.1.0" 4 | authors = ["Hanaasagi "] 5 | 6 | [dependencies] 7 | spin = "0.4.6" 8 | bitflags = "1" 9 | 10 | [dependencies.device] 11 | path = "../device" 12 | -------------------------------------------------------------------------------- /memory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memory" 3 | version = "0.1.0" 4 | authors = ["Hanaasagi "] 5 | 6 | [dependencies] 7 | bitflags = "0.9.1" 8 | x86_64 = "0.1.2" 9 | multiboot2 = "0.3.2" 10 | 11 | [dependencies.vga] 12 | path = "../vga" 13 | -------------------------------------------------------------------------------- /filesystem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "filesystem" 3 | version = "0.1.0" 4 | authors = ["Hanaasagi "] 5 | 6 | [dependencies] 7 | bitflags = "1" 8 | 9 | [dependencies.device] 10 | path = "../device" 11 | 12 | [dependencies.vga] 13 | path = "../vga" 14 | -------------------------------------------------------------------------------- /device/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm)] 3 | #![feature(const_fn)] 4 | 5 | pub mod io; 6 | pub mod pic; 7 | pub mod keyboard; 8 | pub mod tty; 9 | pub mod disk; 10 | pub mod ata; 11 | 12 | #[macro_use] 13 | extern crate vga; 14 | 15 | #[macro_use] 16 | extern crate bitflags; 17 | extern crate spin; 18 | -------------------------------------------------------------------------------- /src/arch/x86_64/long_mode_init.asm: -------------------------------------------------------------------------------- 1 | global long_mode_start 2 | 3 | extern kmain 4 | 5 | section .text 6 | bits 64 7 | long_mode_start: 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 | jmp kmain 16 | 17 | ; print `OKAY` to screen 18 | mov rax, 0x2f592f412f4b2f4f 19 | mov qword [0xb8000], rax 20 | hlt 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | /target/ 12 | **/*.rs.bk 13 | 14 | /build/ 15 | 16 | /target/ 17 | **/*.rs.bk 18 | Cargo.lock 19 | -------------------------------------------------------------------------------- /x86_64-kurumi.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "x86_64", 3 | "cpu": "x86-64", 4 | "llvm-target": "x86_64-unknown-none-gnu", 5 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 6 | "linker-flavor": "gcc", 7 | "no-compiler-rt": true, 8 | "os": "kurumi", 9 | "target-endian": "little", 10 | "target-pointer-width": "64", 11 | "target-c-int-width": "32", 12 | "features": "-mmx,-fxsr,-sse,-sse2,+soft-float", 13 | "disable-redzone": true, 14 | "eliminate-frame-pointer": false 15 | } 16 | -------------------------------------------------------------------------------- /src/arch/x86_64/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 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kurumi" 3 | version = "0.1.0" 4 | authors = ["Hanaasagi "] 5 | 6 | [lib] 7 | crate-type = ["staticlib"] 8 | path = "src/kernel.rs" 9 | 10 | [profile.dev] 11 | panic = "abort" 12 | 13 | [profile.release] 14 | panic = "abort" 15 | 16 | [dependencies] 17 | rlibc = "1.0" 18 | multiboot2 = "0.3.2" 19 | linked_list_allocator = "0.5" 20 | x86_64 = "0.1.2" 21 | 22 | [dependencies.interrupt] 23 | path = "interrupt" 24 | 25 | [dependencies.vga] 26 | path = "vga" 27 | 28 | [dependencies.device] 29 | path = "device" 30 | 31 | [dependencies.memory] 32 | path = "memory" 33 | 34 | [dependencies.filesystem] 35 | path = "filesystem" 36 | -------------------------------------------------------------------------------- /device/src/io.rs: -------------------------------------------------------------------------------- 1 | // Write 8 bits to port 2 | pub unsafe fn outb(port: u16, val: u8) { 3 | asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(val)); 4 | } 5 | 6 | // Read 8 bits from port 7 | pub unsafe fn inb(port: u16) -> u8 { 8 | let ret: u8; 9 | asm!("inb %dx, %al" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); 10 | ret 11 | } 12 | 13 | // Forces the CPU to wait for an I/O operation to complete. 14 | // only use this when there's nothing like 15 | // a status register or an IRQ to tell you the info has been received. 16 | pub unsafe fn io_wait() { 17 | asm!("jmp 1f;1:jmp 2f;2:" :::: "volatile"); 18 | } 19 | 20 | // Read 16 bits from port 21 | pub unsafe fn inw(port: u16) -> u16 { 22 | let ret: u16; 23 | asm!("inw %dx, %ax" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); 24 | ret 25 | } 26 | -------------------------------------------------------------------------------- /interrupt/src/dtables.rs: -------------------------------------------------------------------------------- 1 | use core::mem::size_of; 2 | 3 | use idt::IdtEntry; 4 | 5 | #[repr(C, packed)] 6 | pub struct DescriptorTablePointer { 7 | // Size of the DT. 8 | pub limit: u16, 9 | // Pointer to the memory region containing the IDT. 10 | pub base: *const IdtEntry, 11 | } 12 | 13 | impl DescriptorTablePointer { 14 | fn new(slice: &[IdtEntry]) -> Self { 15 | let len = slice.len() * size_of::(); 16 | assert!(len < 0x10000); 17 | DescriptorTablePointer { 18 | base: slice.as_ptr(), 19 | limit: len as u16, 20 | } 21 | } 22 | 23 | pub fn new_idtp(idt: &[IdtEntry]) -> Self { 24 | Self::new(idt) 25 | } 26 | } 27 | 28 | // Load IDT table. 29 | pub unsafe fn lidt(idt: &DescriptorTablePointer) { 30 | asm!("lidt ($0)" :: "r" (idt) : "memory"); 31 | } 32 | -------------------------------------------------------------------------------- /src/arch/x86_64/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | 3 | SECTIONS { 4 | . = 1M; 5 | 6 | .rodata : 7 | { 8 | /* ensure that the multiboot header is at the beginning */ 9 | KEEP(*(.multiboot_header)) 10 | *(.rodata .rodata.*) 11 | . = ALIGN(4K); 12 | } 13 | 14 | .text : 15 | { 16 | *(.text .text.*) 17 | . = ALIGN(4K); 18 | } 19 | 20 | .data : 21 | { 22 | *(.data .data.*) 23 | . = ALIGN(4K); 24 | } 25 | 26 | .bss : 27 | { 28 | *(.bss .bss.*) 29 | . = ALIGN(4K); 30 | } 31 | 32 | .got : 33 | { 34 | *(.got) 35 | . = ALIGN(4K); 36 | } 37 | 38 | .got.plt : 39 | { 40 | *(.got.plt) 41 | . = ALIGN(4K); 42 | } 43 | 44 | .data.rel.ro : ALIGN(4K) 45 | { 46 | *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) 47 | . = ALIGN(4K); 48 | } 49 | 50 | .gcc_except_table : ALIGN(4K) 51 | { 52 | *(.gcc_except_table) 53 | . = ALIGN(4K); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /memory/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Philipp Oppermann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /makefat32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | IMG="disk.img" # image name 6 | SIZE=64 # image size 7 | SOURCE="test" # source dir 8 | 9 | LOGICAL_SECTOR_SIZE=512 10 | OFFSET=0 11 | 12 | if [ -e "$IMG" ]; then 13 | echo "ERROR: $IMG already exists." 14 | exit 1 15 | fi 16 | 17 | relpath() { 18 | full=$1 19 | if [ "$full" == "$SOURCE" ]; then 20 | echo "" 21 | else 22 | base=${SOURCE%%/}/ 23 | echo "${full##$base}" 24 | fi 25 | } 26 | 27 | # generate source dir 28 | (mkdir $SOURCE && cd "$_" && \ 29 | echo "Hello World" >> README && \ 30 | mkdir program && cd "$_" && \ 31 | echo "I love Rust" >> rust.txt) 32 | 33 | # make img 34 | fallocate -l ${SIZE}M "$IMG" 35 | /sbin/mkfs.fat -F32 -S"$LOGICAL_SECTOR_SIZE" "$IMG" >/dev/null 36 | 37 | # copy file 38 | find "$SOURCE" -type d | while read dir; do 39 | target=$(relpath $dir) 40 | [ -z "$target" ] && continue 41 | mmd -i "$IMG" "::$target" 42 | done 43 | find $SOURCE -type f | while read file; do 44 | target=$(relpath $file) 45 | mcopy -i "$IMG" "$file" "::$target" 46 | done 47 | 48 | # clean 49 | rm -rf $SOURCE 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 花浅葱 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kurumi (くるみ) 2 | 3 | kurumi is a toy os implemented in Rust. It is an experimental project. 4 | 5 | ![Imgur](https://i.imgur.com/seoYuqw.gif) 6 | 7 | ### Progress 8 | 9 | - [X] Boot 10 | - [X] vga output 11 | - [X] interrupt 12 | - [X] keyboard 13 | - [ ] tty 14 | - [ ] context 15 | - [ ] system call 16 | - [X] memory(follow blog_os) 17 | - [X] file system(FAT32) 18 | - [ ] console 19 | - [ ] process 20 | 21 | ### Build 22 | It depend on Rust nightly, Xargo, nasm, xorriso, qemu. 23 | 24 | In debian 25 | ``` 26 | $ apt-get install nasm \ 27 | binutils \ 28 | grub-common \ 29 | xorriso \ 30 | grub-pc-bin \ 31 | qemu 32 | $ cargo install xargo 33 | $ rustup component add rust-src 34 | ``` 35 | 36 | ### Run 37 | 38 | ``` 39 | $ make iso 40 | $ make run 41 | ``` 42 | 43 | ### Reference 44 | [Linux内核设计与实现](https://book.douban.com/subject/6097773/) 45 | [Linux内核0.11完全注释](https://github.com/loveveryday/linux0.11) 46 | [30天自制操作系统](https://book.douban.com/subject/11530329/) 47 | [Stanford CS140e - Operating Systems](https://web.stanford.edu/class/cs140e/) 48 | [Writing an OS in Rust](https://os.phil-opp.com/) 49 | [Redox-kernel](https://github.com/redox-os/kernel) 50 | -------------------------------------------------------------------------------- /memory/src/frame.rs: -------------------------------------------------------------------------------- 1 | use super::PAGE_SIZE; 2 | use super::PhysicalAddress; 3 | 4 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 5 | pub struct Frame { 6 | pub number: usize, 7 | } 8 | 9 | impl Frame { 10 | pub fn containing_address(address: usize) -> Frame { 11 | Frame { number: address / PAGE_SIZE } 12 | } 13 | 14 | pub fn start_address(&self) -> PhysicalAddress { 15 | self.number * PAGE_SIZE 16 | } 17 | 18 | pub fn clone(&self) -> Frame { 19 | Frame { number: self.number } 20 | } 21 | 22 | pub fn range_inclusive(start: Frame, end: Frame) -> FrameIter { 23 | FrameIter { 24 | start: start, 25 | end: end, 26 | } 27 | } 28 | } 29 | 30 | pub struct FrameIter { 31 | start: Frame, 32 | end: Frame, 33 | } 34 | 35 | impl Iterator for FrameIter { 36 | type Item = Frame; 37 | 38 | fn next(&mut self) -> Option { 39 | if self.start <= self.end { 40 | let frame = self.start.clone(); 41 | self.start.number += 1; 42 | Some(frame) 43 | } else { 44 | None 45 | } 46 | } 47 | } 48 | 49 | pub trait FrameAllocator { 50 | fn allocate_frame(&mut self) -> Option; 51 | fn deallocate_frame(&mut self, frame: Frame); 52 | } 53 | -------------------------------------------------------------------------------- /filesystem/src/fat32/bpb.rs: -------------------------------------------------------------------------------- 1 | // BIOS Parameter Block see https://wiki.osdev.org/FAT#Boot_Record 2 | #[derive(Debug, Clone, Copy)] 3 | #[repr(packed, C)] 4 | pub struct Bpb { 5 | pub bootjmp: [u8; 3], 6 | pub oem_identifier: [u8; 8], 7 | pub bytes_per_sector: u16, 8 | pub sectors_per_cluster: u8, 9 | pub reserved_sectors_count: u16, 10 | pub table_count: u8, 11 | pub root_entry_count: u16, 12 | pub total_sectors: u16, 13 | pub media_descriptor_type: u8, 14 | pub sectors_per_fat: u16, 15 | pub sectors_per_track: u16, 16 | pub head_size_count: u16, 17 | pub hidden_sectors_count: u32, 18 | pub total_sectors_large: u32, 19 | } 20 | 21 | // Extended Boot Record 22 | #[derive(Debug, Clone, Copy)] 23 | #[repr(packed, C)] 24 | pub struct Ebpb { 25 | pub bpb: Bpb, 26 | pub sectors_per_fat: u32, 27 | pub flags: u16, 28 | pub version_number: u16, 29 | pub root_dir_cluster: u32, 30 | pub fsinfo_sector: u16, 31 | pub backup_mbr_sector: u16, 32 | pub reserved: [u8; 12], 33 | pub drive_number: u8, 34 | pub flags_nt: u8, 35 | pub signature: u8, 36 | pub volume_id: u32, 37 | pub volume_label: [u8; 11], 38 | pub system_identifier: [u8; 8] 39 | } 40 | -------------------------------------------------------------------------------- /interrupt/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm)] 3 | #![feature(naked_functions)] 4 | #![feature(const_fn)] 5 | #![feature(core_intrinsics)] 6 | 7 | #[macro_use] 8 | extern crate bitflags; 9 | extern crate spin; 10 | 11 | extern crate device; 12 | 13 | #[macro_use] 14 | pub mod macros; 15 | pub mod idt; 16 | mod dtables; 17 | 18 | use spin::Mutex; 19 | use core::intrinsics; 20 | 21 | use idt::IdtEntry; 22 | use dtables::DescriptorTablePointer; 23 | use device::{pic, tty, keyboard}; 24 | 25 | // The Interrupt Descriptor Table 26 | // The CPU will look at this table to find the appropriate interrupt handler. 27 | pub static IDT: Mutex<[IdtEntry; 256]> = Mutex::new([IdtEntry::new(); 256]); 28 | 29 | 30 | pub fn init() { 31 | 32 | let ptr: DescriptorTablePointer = 33 | DescriptorTablePointer::new_idtp(&IDT.lock()[..]); 34 | 35 | unsafe { dtables::lidt(&ptr) }; 36 | 37 | interrupt!(isr32, { 38 | pic::send_eoi(32); 39 | }); 40 | 41 | interrupt!(isr33, { 42 | if let Some(c) = keyboard::read_char() { 43 | tty::TTY_BUF.lock().input(c); 44 | } 45 | pic::send_eoi(33); 46 | }); 47 | 48 | interrupt!(isr46, { 49 | pic::send_eoi(46); 50 | }); 51 | 52 | // IDT Table 53 | IDT.lock()[32].set_func(isr32); 54 | IDT.lock()[33].set_func(isr33); 55 | IDT.lock()[46].set_func(isr46); 56 | 57 | unsafe { sti!() } 58 | } 59 | -------------------------------------------------------------------------------- /device/src/tty.rs: -------------------------------------------------------------------------------- 1 | // tty is a device used to user's input & output 2 | // input come form keyboard 3 | // output to screen via vga.c 4 | 5 | extern crate spin; 6 | extern crate vga; 7 | 8 | use spin::Mutex; 9 | 10 | const NTTY_BUF: u32 = 512; 11 | 12 | #[allow(non_camel_case_types)] 13 | pub struct TTY_Buf { 14 | nread: u32, 15 | nwrite: u32, 16 | buf: [char; NTTY_BUF as usize], 17 | } 18 | 19 | impl TTY_Buf { 20 | 21 | const fn new() -> Self { 22 | TTY_Buf { 23 | nread: 0, 24 | nwrite: 0, 25 | buf: ['\0'; NTTY_BUF as usize], 26 | } 27 | } 28 | 29 | fn check_full(&self) { 30 | if self.nwrite == self.nread + NTTY_BUF { 31 | panic!(); 32 | } 33 | } 34 | 35 | pub fn input(&mut self, ch: char) { 36 | self.check_full(); 37 | let pos = (self.nwrite % NTTY_BUF) as usize; 38 | self.nwrite += 1; 39 | self.buf[pos] = ch; 40 | self.read(); 41 | } 42 | 43 | fn read(&mut self) { 44 | for i in self.nread..self.nwrite { 45 | let ch = self.buf[i as usize]; 46 | match ch { 47 | // backspace 48 | '\x08' => vga::clear_left_once(), 49 | _ => kprint!("{}", ch), 50 | } 51 | } 52 | self.nread = self.nwrite; 53 | } 54 | } 55 | 56 | pub static TTY_BUF: Mutex = Mutex::new(TTY_Buf::new()); 57 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | arch ?= x86_64 2 | kernel := build/kernel-$(arch).bin 3 | iso := build/os-$(arch).iso 4 | target ?= $(arch)-kurumi 5 | rust_os := target/$(target)/release/libkurumi.a 6 | filesystem := build/disk.img 7 | 8 | linker_script := src/arch/$(arch)/linker.ld 9 | grub_cfg := src/arch/$(arch)/grub.cfg 10 | assembly_source_files := $(wildcard src/arch/$(arch)/*.asm) 11 | assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \ 12 | build/arch/$(arch)/%.o, $(assembly_source_files)) 13 | 14 | .PHONY: all clean run iso filesystem 15 | 16 | all: $(kernel) 17 | 18 | clean: 19 | @rm -r build 20 | 21 | run: $(iso) filesystem 22 | @qemu-system-x86_64 -hda $(filesystem) -cdrom $(iso) -boot d 23 | 24 | iso: $(iso) 25 | 26 | $(iso): $(kernel) $(grub_cfg) 27 | @mkdir -p build/isofiles/boot/grub 28 | @cp $(kernel) build/isofiles/boot/kernel.bin 29 | @cp $(grub_cfg) build/isofiles/boot/grub 30 | @grub-mkrescue -o $(iso) build/isofiles 31 | @rm -r build/isofiles 32 | 33 | $(kernel): kernel $(rust_os) $(assembly_object_files) $(linker_script) 34 | @ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os) 35 | 36 | kernel: 37 | @RUST_TARGET_PATH="$(shell pwd)" xargo build --release --target $(target) 38 | 39 | filesystem: 40 | @bash makefat32.sh 41 | @mv disk.img $(filesystem) 42 | 43 | # compile assembly files 44 | build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm 45 | @mkdir -p $(shell dirname $@) 46 | @nasm -f elf64 $< -o $@ 47 | -------------------------------------------------------------------------------- /filesystem/src/file.rs: -------------------------------------------------------------------------------- 1 | // simple file implementation 2 | 3 | use alloc::string::String; 4 | 5 | pub trait File { 6 | fn get_name(&self) -> String; 7 | fn get_size(&self) -> usize; 8 | } 9 | 10 | #[allow(dead_code)] 11 | pub enum FileMode { 12 | Read, 13 | Write, 14 | ReadWrite, 15 | } 16 | 17 | pub struct FilePointer { 18 | file: T, 19 | pos: usize, // cursor 20 | } 21 | 22 | impl FilePointer { 23 | pub fn new(file: T, pos: usize,) -> FilePointer { 24 | FilePointer { 25 | file: file, 26 | pos: pos, 27 | } 28 | } 29 | pub fn get_pos(&self) -> usize { 30 | self.pos 31 | } 32 | 33 | pub fn advance_pointer(&mut self, amount: usize) { 34 | self.pos += amount 35 | } 36 | 37 | pub fn get_file(&self) -> &T { 38 | &self.file 39 | } 40 | } 41 | 42 | #[allow(dead_code)] 43 | pub struct FileDescriptor { 44 | id: u16, 45 | mode: FileMode, 46 | pointer: FilePointer 47 | } 48 | 49 | impl FileDescriptor { 50 | pub fn new(id: u16, mode: FileMode, pointer: FilePointer) -> FileDescriptor { 51 | FileDescriptor { 52 | id: id, 53 | mode: mode, 54 | pointer: pointer, 55 | } 56 | } 57 | 58 | pub fn get_id(&self) -> u16 { 59 | self.id 60 | } 61 | 62 | #[allow(dead_code)] 63 | pub fn get_pointer(&self) -> &FilePointer { 64 | &self.pointer 65 | } 66 | 67 | pub fn get_pointer_mut(&mut self) -> &mut FilePointer { 68 | &mut self.pointer 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /interrupt/src/idt.rs: -------------------------------------------------------------------------------- 1 | bitflags! { 2 | pub struct IdtFlags: u8 { 3 | const PRESENT = 1 << 7; 4 | const RING_0 = 0 << 5; 5 | const RING_1 = 1 << 5; 6 | const RING_2 = 2 << 5; 7 | const RING_3 = 3 << 5; 8 | const SS = 1 << 4; 9 | const INTERRUPT = 0xE; 10 | const TRAP = 0xF; 11 | } 12 | } 13 | 14 | // IdtEntry https://wiki.osdev.org/Interrupt_Descriptor_Table#Structure_AMD6 15 | #[derive(Debug, Clone, Copy)] 16 | #[repr(C, packed)] 17 | pub struct IdtEntry { 18 | offsetl: u16, // offset bits 0..15 19 | selector: u16, // a code segment selector in GDT or LDT 20 | ist: u8, // bits 0..2 holds Interrupt Stack Table offset, rest of bits zero. 21 | flags: u8, // type and attributes 22 | offsetm: u16, // offset bits 16..31 23 | offseth: u32, // offset bits 32..63 24 | zero: u32, // reserved 25 | } 26 | 27 | 28 | impl IdtEntry { 29 | 30 | pub const fn new() -> IdtEntry { 31 | IdtEntry { 32 | offsetl: 0, 33 | selector: 0, 34 | ist: 0, 35 | flags: 0, 36 | offsetm: 0, 37 | offseth: 0, 38 | zero: 0 39 | } 40 | } 41 | 42 | pub fn set_flags(&mut self, flags: IdtFlags) { 43 | self.flags = flags.bits; 44 | } 45 | 46 | pub fn set_offset(&mut self, selector: u16, base: usize) { 47 | self.selector = selector; 48 | self.offsetl = base as u16; 49 | self.offsetm = (base >> 16) as u16; 50 | self.offseth = (base >> 32) as u32; 51 | } 52 | 53 | pub fn set_func(&mut self, func: unsafe extern fn()) { 54 | self.set_flags(IdtFlags::PRESENT | IdtFlags::RING_0 | IdtFlags::INTERRUPT); 55 | self.set_offset(0x08, func as usize); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /memory/src/paging/entry.rs: -------------------------------------------------------------------------------- 1 | use super::Frame; 2 | use multiboot2::ElfSection; 3 | 4 | pub struct Entry(u64); 5 | 6 | impl Entry { 7 | pub fn is_unused(&self) -> bool { 8 | self.0 == 0 9 | } 10 | 11 | pub fn set_unused(&mut self) { 12 | self.0 = 0; 13 | } 14 | 15 | pub fn flags(&self) -> EntryFlags { 16 | EntryFlags::from_bits_truncate(self.0) 17 | } 18 | 19 | pub fn pointed_frame(&self) -> Option { 20 | if self.flags().contains(PRESENT) { 21 | Some(Frame::containing_address( 22 | self.0 as usize & 0x000fffff_fffff000, 23 | )) 24 | } else { 25 | None 26 | } 27 | } 28 | 29 | pub fn set(&mut self, frame: Frame, flags: EntryFlags) { 30 | assert!(frame.start_address() & !0x000fffff_fffff000 == 0); 31 | self.0 = (frame.start_address() as u64) | flags.bits(); 32 | } 33 | } 34 | 35 | bitflags! { 36 | pub struct EntryFlags: u64 { 37 | const PRESENT = 1 << 0; 38 | const WRITABLE = 1 << 1; 39 | const USER_ACCESSIBLE = 1 << 2; 40 | const WRITE_THROUGH = 1 << 3; 41 | const NO_CACHE = 1 << 4; 42 | const ACCESSED = 1 << 5; 43 | const DIRTY = 1 << 6; 44 | const HUGE_PAGE = 1 << 7; 45 | const GLOBAL = 1 << 8; 46 | const NO_EXECUTE = 1 << 63; 47 | } 48 | } 49 | 50 | impl EntryFlags { 51 | pub fn from_elf_section_flags(section: &ElfSection) -> EntryFlags { 52 | use multiboot2::{ 53 | ELF_SECTION_ALLOCATED, 54 | ELF_SECTION_WRITABLE, 55 | ELF_SECTION_EXECUTABLE 56 | }; 57 | 58 | let mut flags = EntryFlags::empty(); 59 | 60 | if section.flags().contains(ELF_SECTION_ALLOCATED) { 61 | // section is loaded to memory 62 | flags = flags | PRESENT; 63 | } 64 | if section.flags().contains(ELF_SECTION_WRITABLE) { 65 | flags = flags | WRITABLE; 66 | } 67 | if !section.flags().contains(ELF_SECTION_EXECUTABLE) { 68 | flags = flags | NO_EXECUTE; 69 | } 70 | 71 | flags 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /interrupt/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! scratch_push { 3 | () => (asm!( 4 | "push rax 5 | push rcx 6 | push rdx 7 | push rdi 8 | push rsi 9 | push r8 10 | push r9 11 | push r10 12 | push r11" 13 | : : : : "intel", "volatile" 14 | )); 15 | } 16 | 17 | #[macro_export] 18 | macro_rules! scratch_pop { 19 | () => (asm!( 20 | "pop r11 21 | pop r10 22 | pop r9 23 | pop r8 24 | pop rsi 25 | pop rdi 26 | pop rdx 27 | pop rcx 28 | pop rax" 29 | : : : : "intel", "volatile" 30 | )); 31 | } 32 | 33 | #[macro_export] 34 | macro_rules! preserved_push { 35 | () => (asm!( 36 | "push rbx 37 | push rbp 38 | push r12 39 | push r13 40 | push r14 41 | push r15" 42 | : : : : "intel", "volatile" 43 | )); 44 | } 45 | 46 | #[macro_export] 47 | macro_rules! preserved_pop { 48 | () => (asm!( 49 | "pop r15 50 | pop r14 51 | pop r13 52 | pop r12 53 | pop rbp 54 | pop rbx" 55 | : : : : "intel", "volatile" 56 | )); 57 | } 58 | 59 | 60 | #[macro_export] 61 | macro_rules! iret { 62 | () => (asm!( 63 | "iretq" 64 | : : : : "intel", "volatile" 65 | )); 66 | } 67 | 68 | #[macro_export] 69 | macro_rules! cli { 70 | () => (asm!( 71 | "cli" 72 | : : : : "intel", "volatile" 73 | )); 74 | } 75 | 76 | #[macro_export] 77 | macro_rules! sti { 78 | () => (asm!( 79 | "sti" 80 | : : : : "intel", "volatile" 81 | )); 82 | } 83 | 84 | #[macro_export] 85 | macro_rules! interrupt { 86 | ($name:ident, $body:expr) => { 87 | 88 | #[naked] 89 | unsafe extern fn $name() { 90 | #[inline(never)] 91 | fn inner() { 92 | $body 93 | } 94 | 95 | scratch_push!(); 96 | preserved_push!(); 97 | cli!(); 98 | inner(); 99 | sti!(); 100 | preserved_pop!(); 101 | scratch_pop!(); 102 | iret!(); 103 | 104 | intrinsics::unreachable(); 105 | } 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /memory/src/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | use alloc::heap::{Alloc, AllocErr, Layout}; 2 | 3 | use core::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | // A simple allocator that allocates memory linearly and ignores freed memory. 6 | #[derive(Debug)] 7 | pub struct BumpAllocator { 8 | heap_start: usize, 9 | heap_end: usize, 10 | next: AtomicUsize, 11 | } 12 | 13 | impl BumpAllocator { 14 | pub const fn new(heap_start: usize, heap_end: usize) -> Self { 15 | Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) } 16 | } 17 | } 18 | 19 | unsafe impl<'a> Alloc for &'a BumpAllocator { 20 | unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { 21 | loop { 22 | // load current state of the `next` field 23 | let current_next = self.next.load(Ordering::Relaxed); 24 | let alloc_start = align_up(current_next, layout.align()); 25 | let alloc_end = alloc_start.saturating_add(layout.size()); 26 | 27 | if alloc_end <= self.heap_end { 28 | // update the `next` pointer if it still has the value `current_next` 29 | let next_now = self.next.compare_and_swap(current_next, alloc_end, 30 | Ordering::Relaxed); 31 | if next_now == current_next { 32 | // next address was successfully updated, allocation succeeded 33 | return Ok(alloc_start as *mut u8); 34 | } 35 | } else { 36 | return Err(AllocErr::Exhausted{ request: layout }) 37 | } 38 | } 39 | } 40 | 41 | unsafe fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) { 42 | // do nothing, leak memory 43 | } 44 | 45 | fn oom(&mut self, _: AllocErr) -> ! { 46 | panic!("Out of memory"); 47 | } 48 | } 49 | 50 | // Align downwards. Returns the greatest x with alignment `align` 51 | // so that x <= addr. The alignment must be a power of 2. 52 | pub fn align_down(addr: usize, align: usize) -> usize { 53 | if align.is_power_of_two() { 54 | addr & !(align - 1) 55 | } else if align == 0 { 56 | addr 57 | } else { 58 | panic!("`align` must be a power of 2"); 59 | } 60 | } 61 | 62 | // Align upwards. Returns the smallest x with alignment `align` 63 | // so that x >= addr. The alignment must be a power of 2. 64 | pub fn align_up(addr: usize, align: usize) -> usize { 65 | align_down(addr + align - 1, align) 66 | } 67 | -------------------------------------------------------------------------------- /memory/src/stack_allocator.rs: -------------------------------------------------------------------------------- 1 | use super::paging::{self, Page, PageIter, ActivePageTable}; 2 | use super::{PAGE_SIZE, FrameAllocator}; 3 | 4 | pub struct StackAllocator { 5 | range: PageIter, 6 | } 7 | 8 | impl StackAllocator { 9 | pub fn new(page_range: PageIter) -> StackAllocator { 10 | StackAllocator { range: page_range } 11 | } 12 | } 13 | 14 | impl StackAllocator { 15 | pub fn alloc_stack(&mut self, active_table: &mut ActivePageTable, 16 | frame_allocator: &mut FA, size_in_pages: usize,) 17 | -> Option where FA: FrameAllocator{ 18 | if size_in_pages == 0 { 19 | return None; /* a zero sized stack makes no sense */ 20 | } 21 | 22 | // clone the range, since we only want to change it on success 23 | let mut range = self.range.clone(); 24 | 25 | // try to allocate the stack pages and a guard page 26 | let guard_page = range.next(); 27 | let stack_start = range.next(); 28 | let stack_end = if size_in_pages == 1 { 29 | stack_start 30 | } else { 31 | // choose the (size_in_pages-2)th element, since index 32 | // starts at 0 and we already allocated the start page 33 | range.nth(size_in_pages - 2) 34 | }; 35 | 36 | match (guard_page, stack_start, stack_end) { 37 | (Some(_), Some(start), Some(end)) => { 38 | // success! write back updated range 39 | self.range = range; 40 | 41 | // map stack pages to physical frames 42 | for page in Page::range_inclusive(start, end) { 43 | active_table.map(page, paging::WRITABLE, frame_allocator); 44 | } 45 | 46 | // create a new stack 47 | let top_of_stack = end.start_address() + PAGE_SIZE; 48 | Some(Stack::new(top_of_stack, start.start_address())) 49 | } 50 | _ => None, /* not enough pages */ 51 | } 52 | } 53 | } 54 | 55 | #[derive(Debug)] 56 | pub struct Stack { 57 | top: usize, 58 | bottom: usize, 59 | } 60 | 61 | impl Stack { 62 | fn new(top: usize, bottom: usize) -> Stack { 63 | assert!(top > bottom); 64 | Stack { 65 | top: top, 66 | bottom: bottom, 67 | } 68 | } 69 | 70 | pub fn top(&self) -> usize { 71 | self.top 72 | } 73 | 74 | #[allow(dead_code)] 75 | pub fn bottom(&self) -> usize { 76 | self.bottom 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /memory/src/paging/temporary_page.rs: -------------------------------------------------------------------------------- 1 | use super::{Page, ActivePageTable, VirtualAddress}; 2 | use super::table::{Table, Level1}; 3 | use super::{Frame, FrameAllocator}; 4 | 5 | pub struct TemporaryPage { 6 | page: Page, 7 | allocator: TinyAllocator, 8 | } 9 | 10 | impl TemporaryPage { 11 | pub fn new(page: Page, allocator: &mut A) 12 | -> TemporaryPage where A: FrameAllocator { 13 | TemporaryPage { 14 | page: page, 15 | allocator: TinyAllocator::new(allocator), 16 | } 17 | } 18 | 19 | // Maps the temporary page to the given frame in the active table. 20 | // Returns the start address of the temporary page. 21 | pub fn map(&mut self, frame: Frame, active_table: &mut ActivePageTable) -> VirtualAddress { 22 | use super::entry::WRITABLE; 23 | 24 | assert!( 25 | active_table.translate_page(self.page).is_none(), 26 | "temporary page is already mapped" 27 | ); 28 | active_table.map_to(self.page, frame, WRITABLE, &mut self.allocator); 29 | self.page.start_address() 30 | } 31 | 32 | // Maps the temporary page to the given page table frame in the active table. 33 | // Returns a reference to the now mapped table. 34 | pub fn map_table_frame( 35 | &mut self, 36 | frame: Frame, 37 | active_table: &mut ActivePageTable, 38 | ) -> &mut Table { 39 | unsafe { &mut *(self.map(frame, active_table) as *mut Table) } 40 | } 41 | 42 | // Unmaps the temporary page in the active table. 43 | pub fn unmap(&mut self, active_table: &mut ActivePageTable) { 44 | active_table.unmap(self.page, &mut self.allocator) 45 | } 46 | } 47 | 48 | struct TinyAllocator([Option; 3]); 49 | 50 | impl TinyAllocator {fn new(allocator: &mut A) -> TinyAllocator 51 | where A: FrameAllocator { 52 | let mut f = || allocator.allocate_frame(); 53 | let frames = [f(), f(), f()]; 54 | TinyAllocator(frames) 55 | } 56 | } 57 | 58 | impl FrameAllocator for TinyAllocator { 59 | fn allocate_frame(&mut self) -> Option { 60 | for frame_option in &mut self.0 { 61 | if frame_option.is_some() { 62 | return frame_option.take(); 63 | } 64 | } 65 | None 66 | } 67 | 68 | fn deallocate_frame(&mut self, frame: Frame) { 69 | for frame_option in &mut self.0 { 70 | if frame_option.is_none() { 71 | *frame_option = Some(frame); 72 | return; 73 | } 74 | } 75 | panic!("Tiny allocator can hold only 3 frames."); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /device/src/pic.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use io::{inb, outb, io_wait}; 4 | 5 | // reinitialize the PIC controllers, giving them specified vector offsets 6 | // rather than 8h and 70h, as configured by default 7 | // 8 | const PIC1: u16 = 0x20; // IO base address for master PIC 9 | const PIC2: u16 = 0xA0; // IO base address for slave PIC 10 | const PIC1_COMMAND: u16 = PIC1; 11 | const PIC1_DATA: u16 = (PIC1+1); 12 | const PIC2_COMMAND: u16 = PIC2; 13 | const PIC2_DATA: u16 = (PIC2+1); 14 | 15 | const ICW1_ICW4: u8 = 0x01; // ICW4 (not) needed 16 | const ICW1_SINGL: u8 = 0x02; // Single (cascade) mode 17 | const ICW1_INTERVAL4: u8 = 0x04; // Call address interval 4 (8) 18 | const ICW1_LEVEL: u8 = 0x08; // Level triggered (edge) mode 19 | const ICW1_INIT: u8 = 0x10; // Initialization - required! 20 | 21 | const ICW4_8086: u8 = 0x01; // 8086/88 (MCS-80/85) mode 22 | const ICW4_AUTO: u8 = 0x02; // Auto (normal) EOI 23 | const ICW4_BUF_SLAVE: u8 = 0x08; // Buffered mode/slave 24 | const ICW4_BUF_MASTER: u8 = 0x0C; // Buffered mode/master 25 | const ICW4_SFNM: u8 = 0x10; // Special fully nested (not) 26 | 27 | // arguments: 28 | // offset1 - vector offset for master PIC 29 | // vectors on the master become offset1..offset1+7 30 | // offset2 - same for slave PIC: offset2..offset2+7 31 | 32 | pub fn remap() { 33 | unsafe { 34 | // save masks 35 | let a1 = inb(PIC1_DATA); 36 | let a2 = inb(PIC2_DATA); 37 | 38 | let offset1: u8 = 0x20; 39 | let offset2: u8 = 0x28; 40 | outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // starts the initialization sequence (in cascade mode) 41 | io_wait(); 42 | outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4); 43 | io_wait(); 44 | outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset 45 | io_wait(); 46 | outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset 47 | io_wait(); 48 | outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) 49 | io_wait(); 50 | outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) 51 | io_wait(); 52 | 53 | // set both PICs to 8086 mode 54 | outb(PIC1_DATA, ICW4_8086); 55 | io_wait(); 56 | outb(PIC2_DATA, ICW4_8086); 57 | io_wait(); 58 | 59 | // restore saved masks. 60 | outb(PIC1_DATA, a1); 61 | outb(PIC2_DATA, a2); 62 | } 63 | } 64 | 65 | // End-of-interrupt command code 66 | const PIC_EOI: u8 = 0x20; 67 | 68 | pub fn send_eoi(interrupt_number: isize) { 69 | // TODO 70 | if interrupt_number >= 8 { 71 | unsafe { outb(PIC2_COMMAND,PIC_EOI); } 72 | } 73 | unsafe {outb(PIC1_COMMAND,PIC_EOI); } 74 | } 75 | -------------------------------------------------------------------------------- /filesystem/src/fat32/cluster.rs: -------------------------------------------------------------------------------- 1 | use super::{Fat32, Disk}; 2 | 3 | pub type Cluster = u32; 4 | 5 | #[derive(Debug, PartialEq, Copy, Clone)] 6 | pub enum FatEntry { 7 | Node(Cluster), 8 | End, 9 | BadBlock, 10 | } 11 | 12 | pub struct ClusterChain<'a> { 13 | current_entry: FatEntry, 14 | fat: &'a Fat32, 15 | drive: &'a Disk, 16 | } 17 | 18 | impl <'a> ClusterChain<'a> { 19 | pub const fn new(cluster: Cluster, fat: &'a Fat32, drive: &'a Disk) -> Self { 20 | ClusterChain { 21 | current_entry: FatEntry::Node(cluster), 22 | fat: fat, 23 | drive: drive, 24 | } 25 | } 26 | 27 | fn check_end(&self, cluster: Cluster) -> bool { 28 | if cluster >= self.fat.get_total_clusters() { 29 | return true; 30 | } 31 | false 32 | } 33 | 34 | // follow https://wiki.osdev.org/FAT#FAT_32_3 35 | fn read_entry(&self, current: Cluster) -> FatEntry { 36 | if self.check_end(current) { 37 | return FatEntry::End; 38 | } 39 | 40 | let first_fat_sector = self.fat.ebpb.bpb.reserved_sectors_count as u32; 41 | let sector_size = self.fat.ebpb.bpb.bytes_per_sector as usize; 42 | let mut fat_table = vec![0u8; sector_size]; 43 | let fat_offset = current * 4; 44 | let fat_sector = first_fat_sector + (fat_offset / sector_size as u32); 45 | let ent_offset = fat_offset % sector_size as u32; 46 | 47 | // at this point you need to read from sector "fat_sector" on the disk into "FAT_table". 48 | let table_value = unsafe { 49 | self.drive.read(fat_sector as u64, &mut fat_table) 50 | .expect("Disk Read Error"); 51 | let table_reference = &fat_table[ent_offset as usize] as *const u8 as *const u32; 52 | // ignore the high 4 bits. 53 | *table_reference & 0x0FFFFFFF 54 | }; 55 | 56 | // If "table_value" is greater than or equal to (>=) 0x0FFFFFF8 then there are no more 57 | // clusters in the chain. This means that the whole file has been read. If "table_value" 58 | // equals (==) 0x0FFFFFF7 then this cluster has been marked as "bad". "Bad" clusters are 59 | // prone to errors and should be avoided. If "table_value" is not one of the above cases 60 | // then it is the cluster number of the next cluster in the file. 61 | if table_value >= 0x0FFFFFF8 { 62 | return FatEntry::End; 63 | } else if table_value == 0x0FFFFFF7 { 64 | return FatEntry::BadBlock; 65 | } 66 | 67 | FatEntry::Node(table_value) 68 | } 69 | } 70 | 71 | impl <'a> Iterator for ClusterChain <'a> { 72 | type Item = Cluster; 73 | 74 | fn next(&mut self) -> Option { 75 | let current_index = match self.current_entry { 76 | FatEntry::Node(current_cluster) => current_cluster, 77 | _ => return None, 78 | }; 79 | self.current_entry = self.read_entry(current_index); 80 | 81 | Some(current_index) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /memory/src/paging/mapper.rs: -------------------------------------------------------------------------------- 1 | use super::{VirtualAddress, PhysicalAddress, Page}; 2 | use super::entry::*; 3 | use super::table::{self, Table, Level4}; 4 | use super::{PAGE_SIZE, Frame, FrameAllocator}; 5 | use core::ptr::Unique; 6 | 7 | pub struct Mapper { 8 | p4: Unique>, 9 | } 10 | 11 | impl Mapper { 12 | pub unsafe fn new() -> Mapper { 13 | Mapper { p4: Unique::new_unchecked(table::P4) } 14 | } 15 | 16 | pub fn p4(&self) -> &Table { 17 | unsafe { self.p4.as_ref() } 18 | } 19 | 20 | pub fn p4_mut(&mut self) -> &mut Table { 21 | unsafe { self.p4.as_mut() } 22 | } 23 | 24 | pub fn translate(&self, virtual_address: VirtualAddress) -> Option { 25 | let offset = virtual_address % PAGE_SIZE; 26 | self.translate_page(Page::containing_address(virtual_address)) 27 | .map(|frame| frame.number * PAGE_SIZE + offset) 28 | } 29 | 30 | pub fn translate_page(&self, page: Page) -> Option { 31 | let p3 = self.p4().next_table(page.p4_index()); 32 | 33 | p3.and_then(|p3| p3.next_table(page.p3_index())) 34 | .and_then(|p2| p2.next_table(page.p2_index())) 35 | .and_then(|p1| p1[page.p1_index()].pointed_frame()) 36 | } 37 | 38 | pub fn map_to(&mut self, page: Page, frame: Frame, 39 | flags: EntryFlags, allocator: &mut A) where A: FrameAllocator { 40 | let p3 = self.p4_mut().next_table_create(page.p4_index(), allocator); 41 | let p2 = p3.next_table_create(page.p3_index(), allocator); 42 | let p1 = p2.next_table_create(page.p2_index(), allocator); 43 | 44 | assert!(p1[page.p1_index()].is_unused()); 45 | p1[page.p1_index()].set(frame, flags | PRESENT); 46 | } 47 | 48 | pub fn map(&mut self, page: Page, flags: EntryFlags, 49 | allocator: &mut A) where A: FrameAllocator { 50 | let frame = allocator.allocate_frame().expect("out of memory"); 51 | self.map_to(page, frame, flags, allocator) 52 | } 53 | 54 | pub fn identity_map(&mut self, frame: Frame, flags: EntryFlags, 55 | allocator: &mut A) where A: FrameAllocator { 56 | let page = Page::containing_address(frame.start_address()); 57 | self.map_to(page, frame, flags, allocator) 58 | } 59 | 60 | pub fn unmap(&mut self, page: Page, _allocator: &mut A) where A: FrameAllocator { 61 | use x86_64::VirtualAddress; 62 | use x86_64::instructions::tlb; 63 | 64 | assert!(self.translate(page.start_address()).is_some()); 65 | 66 | let p1 = self.p4_mut() 67 | .next_table_mut(page.p4_index()) 68 | .and_then(|p3| p3.next_table_mut(page.p3_index())) 69 | .and_then(|p2| p2.next_table_mut(page.p2_index())) 70 | .expect("mapping code does not support huge pages"); 71 | //let frame = p1[page.p1_index()].pointed_frame().unwrap(); 72 | p1[page.p1_index()].set_unused(); 73 | tlb::flush(VirtualAddress(page.start_address())); 74 | // TODO free p(1,2,3) table if empty 75 | // allocator.deallocate_frame(frame); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /memory/src/paging/table.rs: -------------------------------------------------------------------------------- 1 | use super::entry::*; 2 | use super::ENTRY_COUNT; 3 | use super::FrameAllocator; 4 | use core::ops::{Index, IndexMut}; 5 | use core::marker::PhantomData; 6 | 7 | pub const P4: *mut Table = 0xffffffff_fffff000 as *mut _; 8 | 9 | pub struct Table { 10 | entries: [Entry; ENTRY_COUNT], 11 | level: PhantomData, 12 | } 13 | 14 | impl Table where L: TableLevel { 15 | pub fn zero(&mut self) { 16 | for entry in self.entries.iter_mut() { 17 | entry.set_unused(); 18 | } 19 | } 20 | } 21 | 22 | /* 23 | * Addresses are expected to be canonical (bits 48-63 must be the same as bit 47), otherwise the 24 | * CPU will #GP when we ask it to translate it. 25 | */ 26 | fn make_address_canonical(address : usize) -> usize { 27 | let sign_extension = 0o177777_000_000_000_000_0000 * ((address >> 47) & 0b1); 28 | (address & ((1 << 48) - 1)) | sign_extension 29 | } 30 | 31 | impl Table where L: HierarchicalLevel { 32 | fn next_table_address(&self, index: usize) -> Option { 33 | let entry_flags = self[index].flags(); 34 | if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) { 35 | let table_address = self as *const _ as usize; 36 | Some(make_address_canonical((table_address << 9) | (index << 12))) 37 | } else { 38 | None 39 | } 40 | } 41 | 42 | pub fn next_table(&self, index: usize) -> Option<&Table> { 43 | self.next_table_address(index) 44 | .map(|address| unsafe { &*(address as *const _) }) 45 | } 46 | 47 | pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table> { 48 | self.next_table_address(index) 49 | .map(|address| unsafe { &mut *(address as *mut _) }) 50 | } 51 | 52 | pub fn next_table_create(&mut self, index: usize, allocator: &mut A) 53 | -> &mut Table where A: FrameAllocator { 54 | if self.next_table(index).is_none() { 55 | assert!( 56 | !self.entries[index].flags().contains(HUGE_PAGE), 57 | "mapping code does not support huge pages" 58 | ); 59 | let frame = allocator.allocate_frame().expect("no frames available"); 60 | self.entries[index].set(frame, PRESENT | WRITABLE); 61 | self.next_table_mut(index).unwrap().zero(); 62 | } 63 | self.next_table_mut(index).unwrap() 64 | } 65 | } 66 | 67 | impl Index for Table where L: TableLevel { 68 | type Output = Entry; 69 | 70 | fn index(&self, index: usize) -> &Entry { 71 | &self.entries[index] 72 | } 73 | } 74 | 75 | impl IndexMut for Table where L: TableLevel { 76 | fn index_mut(&mut self, index: usize) -> &mut Entry { 77 | &mut self.entries[index] 78 | } 79 | } 80 | 81 | pub trait TableLevel {} 82 | 83 | pub enum Level4 {} 84 | #[allow(dead_code)] 85 | pub enum Level3 {} 86 | #[allow(dead_code)] 87 | pub enum Level2 {} 88 | pub enum Level1 {} 89 | 90 | impl TableLevel for Level4 {} 91 | impl TableLevel for Level3 {} 92 | impl TableLevel for Level2 {} 93 | impl TableLevel for Level1 {} 94 | 95 | pub trait HierarchicalLevel: TableLevel { 96 | type NextLevel: TableLevel; 97 | } 98 | 99 | impl HierarchicalLevel for Level4 { 100 | type NextLevel = Level3; 101 | } 102 | 103 | impl HierarchicalLevel for Level3 { 104 | type NextLevel = Level2; 105 | } 106 | 107 | impl HierarchicalLevel for Level2 { 108 | type NextLevel = Level1; 109 | } 110 | -------------------------------------------------------------------------------- /memory/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(const_fn)] 3 | #![feature(ptr_internals)] 4 | #![feature(alloc)] 5 | #![feature(allocator_api)] 6 | #![feature(global_allocator)] 7 | 8 | extern crate alloc; 9 | #[macro_use] 10 | extern crate bitflags; 11 | #[macro_use] 12 | extern crate vga; 13 | extern crate x86_64; 14 | extern crate multiboot2; 15 | 16 | use multiboot2::BootInformation; 17 | 18 | mod area_frame_allocator; 19 | mod paging; 20 | pub mod frame; 21 | pub mod heap_allocator; 22 | mod stack_allocator; 23 | 24 | pub use area_frame_allocator::AreaFrameAllocator; 25 | pub use paging::remap_the_kernel; 26 | pub use stack_allocator::Stack; 27 | pub use frame::{FrameAllocator, Frame, FrameIter}; 28 | use paging::PhysicalAddress; 29 | 30 | pub const PAGE_SIZE: usize = 4096; 31 | 32 | pub fn init(boot_info: &BootInformation, heap_start: usize, heap_size: usize) -> MemoryController { 33 | //assert_has_not_been_called!("memory::init must be called only once"); 34 | 35 | let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required"); 36 | let elf_sections_tag = boot_info 37 | .elf_sections_tag() 38 | .expect("Elf sections tag required"); 39 | 40 | let kernel_start = elf_sections_tag 41 | .sections() 42 | .filter(|s| s.is_allocated()) 43 | .map(|s| s.addr) 44 | .min() 45 | .unwrap(); 46 | let kernel_end = elf_sections_tag 47 | .sections() 48 | .filter(|s| s.is_allocated()) 49 | .map(|s| s.addr + s.size) 50 | .max() 51 | .unwrap(); 52 | 53 | kprintln!( 54 | "kernel start: {:#x}, kernel end: {:#x}", 55 | kernel_start, 56 | kernel_end 57 | ); 58 | kprintln!( 59 | "multiboot start: {:#x}, multiboot end: {:#x}", 60 | boot_info.start_address(), 61 | boot_info.end_address() 62 | ); 63 | 64 | let mut frame_allocator = AreaFrameAllocator::new( 65 | kernel_start as usize, 66 | kernel_end as usize, 67 | boot_info.start_address(), 68 | boot_info.end_address(), 69 | memory_map_tag.memory_areas(), 70 | ); 71 | 72 | let mut active_table = paging::remap_the_kernel(&mut frame_allocator, boot_info); 73 | 74 | use self::paging::Page; 75 | 76 | let heap_start_page = Page::containing_address(heap_start); 77 | let heap_end_page = Page::containing_address(heap_start + heap_size - 1); 78 | 79 | for page in Page::range_inclusive(heap_start_page, heap_end_page) { 80 | active_table.map(page, paging::WRITABLE, &mut frame_allocator); 81 | } 82 | 83 | let stack_allocator = { 84 | let stack_alloc_start = heap_end_page + 1; 85 | let stack_alloc_end = stack_alloc_start + 100; 86 | let stack_alloc_range = Page::range_inclusive(stack_alloc_start, stack_alloc_end); 87 | stack_allocator::StackAllocator::new(stack_alloc_range) 88 | }; 89 | 90 | MemoryController { 91 | active_table: active_table, 92 | frame_allocator: frame_allocator, 93 | stack_allocator: stack_allocator, 94 | } 95 | } 96 | 97 | pub struct MemoryController { 98 | active_table: paging::ActivePageTable, 99 | frame_allocator: AreaFrameAllocator, 100 | stack_allocator: stack_allocator::StackAllocator, 101 | } 102 | 103 | impl MemoryController { 104 | pub fn alloc_stack(&mut self, size_in_pages: usize) -> Option { 105 | let &mut MemoryController { 106 | ref mut active_table, 107 | ref mut frame_allocator, 108 | ref mut stack_allocator, 109 | } = self; 110 | stack_allocator.alloc_stack(active_table, frame_allocator, size_in_pages) 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /filesystem/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(const_fn)] 3 | #![feature(alloc)] 4 | #![allow(safe_packed_borrows)] 5 | 6 | #[macro_use] 7 | extern crate vga; 8 | extern crate device; 9 | 10 | #[macro_use] 11 | extern crate alloc; 12 | #[macro_use] 13 | extern crate bitflags; 14 | 15 | mod fat32; 16 | mod file; 17 | 18 | use alloc::Vec; 19 | use device::ata::ATA; 20 | use device::disk::Disk; 21 | use file::{File, FileMode, FilePointer, FileDescriptor}; 22 | 23 | trait FileSystem { 24 | type FileType: File; 25 | 26 | fn open_file(&self, drive: &Disk, file_name: &str) -> Option>; 27 | fn read_file(&self, drive: &Disk, file_pointer: &FilePointer, buffer: &mut [u8]) -> Option; 28 | } 29 | 30 | struct FsManager<'a, T: 'a + FileSystem> { 31 | filesystem: &'a T, 32 | drive: &'a Disk, 33 | descriptors: Vec>, 34 | } 35 | 36 | impl <'a, T: FileSystem> FsManager<'a, T> { 37 | fn open_file(&mut self, file_name: &str) -> Option { 38 | if let Some(file_pointer) = self.filesystem.open_file(self.drive, file_name) { 39 | let mut min_index = self.descriptors.len(); 40 | for (index, descriptor) in self.descriptors.iter().enumerate() { 41 | if descriptor.get_id() as usize > index { 42 | min_index = index; 43 | break; 44 | } 45 | } 46 | 47 | let descriptor = FileDescriptor::new( 48 | min_index as u16, 49 | FileMode::ReadWrite, 50 | file_pointer 51 | ); 52 | 53 | self.descriptors.insert(min_index, descriptor); 54 | return Some(min_index as u16); 55 | } 56 | None 57 | } 58 | 59 | fn close_descriptor(&mut self, descriptor: u16) { 60 | let result = self.descriptors.iter().position(|x| x.get_id() == descriptor); 61 | if let Some(index) = result { 62 | self.descriptors.remove(index); 63 | } else { 64 | kprintln!("Descriptor {} is not open.", descriptor); 65 | } 66 | } 67 | 68 | // TODO read folder 69 | 70 | fn read_file(&mut self, descriptor: u16, buffer: &mut [u8]) -> Option { 71 | if let Some(descriptor) = self.descriptors.iter_mut().find(|x| x.get_id() == descriptor) { 72 | let file_pointer = descriptor.get_pointer_mut(); 73 | if let Some(size) = self.filesystem.read_file(self.drive, file_pointer, buffer) { 74 | file_pointer.advance_pointer(size); 75 | return Some(size); 76 | } else { 77 | kprintln!("Unable to read file."); 78 | } 79 | } else { 80 | kprintln!("Descriptor {} is not open.", descriptor); 81 | } 82 | None 83 | } 84 | } 85 | 86 | pub fn test_read() { 87 | let fat32 = unsafe { fat32::Fat32::new(&ATA) }; 88 | kprintln!("{:?}", fat32.ebpb); 89 | let mut fat = FsManager { 90 | filesystem: &fat32, 91 | drive: &ATA, 92 | descriptors: Vec::new(), 93 | }; 94 | 95 | let path = "README"; 96 | kprintln!("Opening file \"{}\".", path); 97 | if let Some(opened_descriptor) = fat.open_file(path) { 98 | //kprintln!("Printing contents of file:"); 99 | 100 | let mut buffer = [0u8; 512]; 101 | let size = fat.read_file(opened_descriptor, &mut buffer).unwrap_or(0); 102 | let buffer = &buffer[0..size]; 103 | //kprintln!("file size {}", size); 104 | use core::str; 105 | kprintln!("{}", str::from_utf8(&buffer).expect("Invalid UTF-8.")); 106 | fat.close_descriptor(opened_descriptor); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /memory/src/area_frame_allocator.rs: -------------------------------------------------------------------------------- 1 | use super::{Frame, FrameAllocator}; 2 | use multiboot2::{MemoryAreaIter, MemoryArea}; 3 | 4 | // A frame allocator that uses the memory areas from the multiboot information structure as 5 | // source. The {kernel, multiboot}_{start, end} fields are used to avoid returning memory that is 6 | // already in use. 7 | // 8 | // `kernel_end` and `multiboot_end` are _inclusive_ bounds. 9 | pub struct AreaFrameAllocator { 10 | next_free_frame: Frame, 11 | current_area: Option<&'static MemoryArea>, 12 | areas: MemoryAreaIter, 13 | kernel_start: Frame, 14 | kernel_end: Frame, 15 | multiboot_start: Frame, 16 | multiboot_end: Frame, 17 | } 18 | 19 | impl AreaFrameAllocator { 20 | pub fn new(kernel_start: usize, kernel_end: usize, multiboot_start: usize, 21 | multiboot_end: usize, memory_areas: MemoryAreaIter) -> AreaFrameAllocator { 22 | let mut allocator = AreaFrameAllocator { 23 | next_free_frame: Frame::containing_address(0), 24 | current_area: None, 25 | areas: memory_areas, 26 | kernel_start: Frame::containing_address(kernel_start), 27 | kernel_end: Frame::containing_address(kernel_end), 28 | multiboot_start: Frame::containing_address(multiboot_start), 29 | multiboot_end: Frame::containing_address(multiboot_end), 30 | }; 31 | allocator.choose_next_area(); 32 | allocator 33 | } 34 | 35 | fn choose_next_area(&mut self) { 36 | self.current_area = self.areas 37 | .clone() 38 | .filter(|area| { 39 | let address = area.base_addr + area.length - 1; 40 | Frame::containing_address(address as usize) >= self.next_free_frame 41 | }) 42 | .min_by_key(|area| area.base_addr); 43 | 44 | if let Some(area) = self.current_area { 45 | let start_frame = Frame::containing_address(area.base_addr as usize); 46 | if self.next_free_frame < start_frame { 47 | self.next_free_frame = start_frame; 48 | } 49 | } 50 | } 51 | } 52 | 53 | impl FrameAllocator for AreaFrameAllocator { 54 | fn allocate_frame(&mut self) -> Option { 55 | if let Some(area) = self.current_area { 56 | // "clone" the frame to return it if it's free. Frame doesn't 57 | // implement Clone, but we can construct an identical frame. 58 | let frame = Frame { number: self.next_free_frame.number }; 59 | 60 | // the last frame of the current area 61 | let current_area_last_frame = { 62 | let address = area.base_addr + area.length - 1; 63 | Frame::containing_address(address as usize) 64 | }; 65 | 66 | if frame > current_area_last_frame { 67 | // all frames of current area are used, switch to next area 68 | self.choose_next_area(); 69 | } else if frame >= self.kernel_start && frame <= self.kernel_end { 70 | // `frame` is used by the kernel 71 | self.next_free_frame = Frame { number: self.kernel_end.number + 1 }; 72 | } else if frame >= self.multiboot_start && frame <= self.multiboot_end { 73 | // `frame` is used by the multiboot information structure 74 | self.next_free_frame = Frame { number: self.multiboot_end.number + 1 }; 75 | } else { 76 | // frame is unused, increment `next_free_frame` and return it 77 | self.next_free_frame.number += 1; 78 | return Some(frame); 79 | } 80 | // `frame` was not valid, try it again with the updated `next_free_frame` 81 | self.allocate_frame() 82 | } else { 83 | None // no free frames left 84 | } 85 | } 86 | 87 | fn deallocate_frame(&mut self, _frame: Frame) { 88 | unimplemented!() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /device/src/keyboard.rs: -------------------------------------------------------------------------------- 1 | // PS/2 keyboard scancode 2 | // http://www.computer-engineering.org/ps2keyboard/scancodes1.html 3 | 4 | use io::inb; 5 | use spin::Mutex; 6 | 7 | // Scancodes range 0x01 ... 0x0E 8 | const ASCII_PART_1: &'static [u8; 14] = b"\x1B1234567890-=\x08"; 9 | // Scancodes range 0x0F ... 0x1C 10 | const ASCII_PART_2: &'static [u8; 14] = b"\tqwertyuiop[]\n"; 11 | // Scancodes range 0x1E ... 0x29 12 | const ASCII_PART_3: &'static [u8; 12] = b"asdfghjkl;'`"; 13 | // Scancodes range 0x2C ... 0x35 14 | const ASCII_PART_4: &'static [u8; 10] = b"zxcvbnm,./"; 15 | 16 | #[derive(Copy, Clone)] 17 | struct Scancode(u8); 18 | 19 | impl Scancode { 20 | 21 | fn to_ascii(&self) -> Option { 22 | let code = self.0 as usize; 23 | match code { 24 | 0x01 ... 0x0e => Some(ASCII_PART_1[code - 0x01]), 25 | 0x0f ... 0x1c => Some(ASCII_PART_2[code - 0x0f]), 26 | 0x1e ... 0x29 => Some(ASCII_PART_3[code - 0x1e]), 27 | 0x2c ... 0x35 => Some(ASCII_PART_4[code - 0x2c]), 28 | 0x2b => Some(b'\\'), 29 | 0x39 => Some(b' '), // SPACE 30 | _ => None, 31 | } 32 | } 33 | } 34 | 35 | // PS/2 keyboard state 36 | struct Keyboard { 37 | scancode: Scancode, 38 | state: Modifiers 39 | } 40 | 41 | impl Keyboard { 42 | 43 | #[inline] 44 | fn read_scancode(&mut self) { 45 | self.scancode = Scancode(unsafe {inb(0x60)}); 46 | } 47 | 48 | #[inline] 49 | fn update(&mut self) { 50 | self.state.update(self.scancode); 51 | } 52 | 53 | fn read_char(&mut self) -> Option{ 54 | self.read_scancode(); 55 | self.update(); 56 | self.scancode.to_ascii().map(|ascii| { 57 | self.state.modify(ascii) as char 58 | }) 59 | } 60 | } 61 | 62 | bitflags! { 63 | 64 | struct Modifiers: u8 { 65 | const L_SHIFT = 0b_1000_0000; 66 | const R_SHIFT = 0b_0100_0000; 67 | const R_CTRL = 0b_0010_0000; 68 | const L_CTRL = 0b_0001_0000; 69 | const R_ALT = 0b_0000_1000; 70 | const L_ALT = 0b_0000_0100; 71 | const CAPSLOCK = 0b_0000_0010; 72 | const NUMLOCK = 0b_0000_0001; 73 | } 74 | } 75 | 76 | impl Modifiers { 77 | 78 | const fn new() -> Self { 79 | Modifiers { bits: 0x00 } 80 | } 81 | 82 | // Returns true if either shift key is pressed. 83 | #[inline] 84 | fn is_shifted(&self) -> bool { 85 | self.contains(Self::L_SHIFT) || self.contains(Self::R_SHIFT) 86 | } 87 | 88 | // Returns true if the keyboard's state is currently uppercase. 89 | #[inline] 90 | fn is_uppercase(&self) -> bool { 91 | self.is_shifted() ^ self.contains(Self::CAPSLOCK) 92 | } 93 | 94 | fn update(&mut self, scancode: Scancode) { 95 | match scancode { 96 | Scancode(0x1D) => self.insert(Self::L_CTRL), 97 | Scancode(0x2A) => self.insert(Self::L_SHIFT), 98 | Scancode(0x36) => self.insert(Self::R_SHIFT), 99 | Scancode(0x38) => self.insert(Self::L_ALT), 100 | // Caps lock toggles on leading edge 101 | Scancode(0x3A) => self.toggle(Self::CAPSLOCK), 102 | Scancode(0x9D) => self.remove(Self::L_CTRL), 103 | Scancode(0xAA) => self.remove(Self::L_SHIFT), 104 | Scancode(0xB6) => self.remove(Self::R_SHIFT), 105 | Scancode(0xB8) => self.remove(Self::L_ALT), 106 | _ => {}, 107 | } 108 | } 109 | 110 | // Apply the keyboard's modifiers to an ASCII scancode. 111 | fn modify(&self, ascii: u8) -> u8 { 112 | match ascii { 113 | b'a' ... b'z' if self.is_uppercase() => ascii - b'a' + b'A', 114 | b'1' ... b'9' if self.is_shifted() => ascii - b'1' + b'!', 115 | b'0' if self.is_shifted() => b')', 116 | _ => ascii 117 | } 118 | } 119 | } 120 | 121 | static KEYBOARD: Mutex = Mutex::new(Keyboard { 122 | scancode: Scancode(0x00), 123 | state: Modifiers::new() 124 | }); 125 | 126 | pub fn read_char() -> Option { 127 | KEYBOARD.lock().read_char() 128 | } 129 | -------------------------------------------------------------------------------- /vga/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(const_fn)] 3 | #![feature(unique)] 4 | #![feature(const_unique_new)] 5 | #![feature(ptr_internals)] 6 | 7 | extern crate spin; 8 | 9 | use spin::Mutex; 10 | use core::fmt::{self, Write}; 11 | use core::ptr::Unique; 12 | 13 | #[allow(dead_code)] 14 | #[repr(u8)] 15 | pub enum Color { 16 | Black = 0, 17 | Blue = 1, 18 | Green = 2, 19 | Cyan = 3, 20 | Red = 4, 21 | Magenta = 5, 22 | Brown = 6, 23 | LightGray = 7, 24 | DarkGray = 8, 25 | LightBlue = 9, 26 | LightGreen = 10, 27 | LightCyan = 11, 28 | LightRed = 12, 29 | Pink = 13, 30 | Yellow = 14, 31 | White = 15, 32 | } 33 | 34 | #[derive(Debug, Clone, Copy)] 35 | struct ColorCode(u8); 36 | 37 | impl ColorCode { 38 | const fn new(fgcolor: Color, bgcolor: Color) -> ColorCode { 39 | ColorCode((bgcolor as u8) << 4 | (fgcolor as u8)) 40 | } 41 | } 42 | 43 | #[repr(C)] 44 | #[derive(Debug, Clone, Copy)] 45 | struct ScreenChar { 46 | ascii_char: u8, 47 | color_code: ColorCode, 48 | } 49 | 50 | const BUFFER_HEIGHT: usize = 25; 51 | const BUFFER_WIDTH: usize = 80; 52 | 53 | struct Buffer { 54 | chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] 55 | } 56 | 57 | pub struct Writer { 58 | column_position: usize, 59 | color_code: ColorCode, 60 | buffer: Unique, 61 | } 62 | 63 | impl fmt::Write for Writer { 64 | fn write_str(&mut self, s: &str) -> fmt::Result { 65 | for byte in s.bytes() { 66 | self.write_byte(byte) 67 | } 68 | Ok(()) 69 | } 70 | } 71 | 72 | impl Writer { 73 | 74 | pub fn write_byte(&mut self, byte: u8) { 75 | match byte { 76 | b'\n' => self.new_line(), 77 | byte => { 78 | if self.column_position >= BUFFER_WIDTH { 79 | self.new_line(); 80 | } 81 | 82 | let row = BUFFER_HEIGHT - 1; 83 | let col = self.column_position; 84 | 85 | let color_code = self.color_code; 86 | self.buffer().chars[row][col] = ScreenChar { 87 | ascii_char: byte, 88 | color_code: color_code, 89 | }; 90 | self.column_position += 1; 91 | } 92 | } 93 | } 94 | 95 | fn buffer(&mut self) -> &mut Buffer { 96 | unsafe{ self.buffer.as_mut() } 97 | } 98 | 99 | fn new_line(&mut self) { 100 | for row in 1..BUFFER_HEIGHT { 101 | for col in 0..BUFFER_WIDTH { 102 | let buffer = self.buffer(); 103 | let character = buffer.chars[row][col]; 104 | buffer.chars[row - 1][col] = character; 105 | } 106 | } 107 | self.clear_row(BUFFER_HEIGHT-1); 108 | self.column_position = 0; 109 | } 110 | 111 | fn clear_row(&mut self, row: usize) { 112 | let blank = ScreenChar { 113 | ascii_char: b' ', 114 | color_code: self.color_code, 115 | }; 116 | for col in 0..BUFFER_WIDTH { 117 | self.buffer().chars[row][col] = blank; 118 | } 119 | } 120 | } 121 | 122 | pub static WRITER: Mutex = Mutex::new(Writer { 123 | column_position: 0, 124 | color_code: ColorCode::new(Color::White, Color::Blue), 125 | buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) }, 126 | }); 127 | 128 | 129 | #[macro_export] 130 | macro_rules! kprint { 131 | ($($arg:tt)*) => ({ 132 | $crate::kprint(format_args!($($arg)*)); 133 | }); 134 | } 135 | 136 | #[macro_export] 137 | macro_rules! kprintln { 138 | ($fmt:expr) => (kprint!(concat!($fmt, "\n"))); 139 | ($fmt:expr, $($arg:tt)*) => (kprint!(concat!($fmt, "\n"), $($arg)*)); 140 | } 141 | 142 | 143 | pub fn clear_screen() { 144 | for _ in 0..BUFFER_HEIGHT { 145 | kprintln!(""); 146 | } 147 | } 148 | 149 | pub fn kprint(args: fmt::Arguments) { 150 | WRITER.lock().write_fmt(args).unwrap(); 151 | } 152 | 153 | pub fn clear_left_once() { 154 | let mut writer = WRITER.lock(); 155 | if writer.column_position <= 0 { 156 | return 157 | } 158 | writer.column_position -= 1; 159 | // can't use kprint here 160 | // otherwist cause deadlock 161 | writer.write_byte(' ' as u8); 162 | writer.column_position -= 1; 163 | } 164 | -------------------------------------------------------------------------------- /src/kernel.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm)] 3 | #![feature(alloc)] 4 | #![feature(lang_items)] 5 | #![feature(global_allocator)] 6 | 7 | #[macro_use] 8 | extern crate vga; 9 | extern crate interrupt; 10 | extern crate device; 11 | extern crate memory; 12 | extern crate filesystem; 13 | 14 | #[macro_use] 15 | extern crate alloc; /* format */ 16 | extern crate rlibc; 17 | extern crate multiboot2; 18 | extern crate linked_list_allocator; 19 | extern crate x86_64; 20 | 21 | use device::pic; 22 | use linked_list_allocator::LockedHeap; 23 | 24 | const HEAP_START: usize = 0o_000_001_000_000_0000; 25 | const HEAP_SIZE: usize = 100 * 1024; // 100 KiB 26 | 27 | #[global_allocator] 28 | static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 29 | 30 | #[no_mangle] 31 | pub extern fn kmain(multiboot_info_addr: usize) -> ! { 32 | vga::clear_screen(); 33 | kprintln!("Booting ..."); 34 | pic::remap(); kprintln!("PIC INIT {:>64}", "[ok]"); 35 | interrupt::init(); kprintln!("INTERRUPT INIT {:>64}", "[ok]"); 36 | kprintln!(r" 37 | | | ___ _ _ __ _ _ _ __ ___ (_) 38 | | |/ | | | | '__| | | | '_ ` _ \| | 39 | | <| |_| | | | |_| | | | | | | | 40 | |_|\_\\__,_|_| \__,_|_| |_| |_|_| 41 | "); 42 | 43 | //show_sys_info(multiboot_info_addr); 44 | 45 | enable_nxe_bit(); 46 | enable_write_protect_bit(); 47 | 48 | let boot_info = unsafe{ multiboot2::load(multiboot_info_addr) }; 49 | memory::init(boot_info, HEAP_START, HEAP_SIZE); 50 | unsafe { HEAP_ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); } 51 | for _ in 0..10000 { 52 | format!("Some String"); 53 | } 54 | 55 | filesystem::test_read(); 56 | kprint!("$ "); 57 | loop {} 58 | } 59 | 60 | fn enable_nxe_bit() { 61 | use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr}; 62 | 63 | let nxe_bit = 1 << 11; 64 | unsafe { 65 | let efer = rdmsr(IA32_EFER); 66 | wrmsr(IA32_EFER, efer | nxe_bit); 67 | } 68 | } 69 | 70 | fn enable_write_protect_bit() { 71 | use x86_64::registers::control_regs::{cr0, cr0_write, Cr0}; 72 | 73 | unsafe { cr0_write(cr0() | Cr0::WRITE_PROTECT) }; 74 | } 75 | 76 | #[allow(dead_code)] 77 | fn show_sys_info(multiboot_info_addr: usize) { 78 | for _ in 0..80 { kprint!("="); } 79 | let boot_info = unsafe{ multiboot2::load(multiboot_info_addr) }; 80 | let memory_map_tag = boot_info.memory_map_tag() 81 | .expect("Memory map tag required"); 82 | 83 | kprintln!("memory areas:"); 84 | for area in memory_map_tag.memory_areas() { 85 | kprintln!(" start: 0x{:08x}, length: 0x{:08x}", 86 | area.base_addr, area.length); 87 | } 88 | 89 | let elf_sections_tag = boot_info.elf_sections_tag() 90 | .expect("Elf-sections tag required"); 91 | 92 | kprintln!("kernel sections:"); 93 | for section in elf_sections_tag.sections() { 94 | kprintln!(" addr: 0x{:08x}, size: 0x{:08x}, flags: 0x{:08x}", 95 | section.addr, section.size, section.flags); 96 | } 97 | 98 | let kernel_start = elf_sections_tag.sections().map(|s| s.addr) 99 | .min().unwrap(); 100 | let kernel_end = elf_sections_tag.sections().map(|s| s.addr + s.size) 101 | .max().unwrap(); 102 | 103 | let multiboot_start = multiboot_info_addr; 104 | let multiboot_end = multiboot_start + (boot_info.total_size as usize); 105 | 106 | kprintln!("kernel starts at 0x{:08x}, ends at 0x{:08x}", 107 | kernel_start, kernel_end); 108 | kprintln!("multiboot starts at 0x{:08x}, ends at 0x{:08x}", 109 | multiboot_start, multiboot_end); 110 | 111 | let mut frame_allocator = memory::AreaFrameAllocator::new( 112 | kernel_start as usize, kernel_end as usize, multiboot_start, 113 | multiboot_end, memory_map_tag.memory_areas()); 114 | use memory::frame::FrameAllocator; 115 | kprintln!("{:?}", frame_allocator.allocate_frame().unwrap()); 116 | kprintln!("{:?}", frame_allocator.allocate_frame().unwrap()); 117 | 118 | for _ in 0..80 { kprint!("="); } 119 | } 120 | 121 | 122 | #[lang = "eh_personality"] 123 | #[no_mangle] 124 | pub extern fn eh_personality() { 125 | 126 | } 127 | 128 | #[lang = "panic_fmt"] 129 | #[no_mangle] 130 | pub extern fn rust_begin_panic(msg: core::fmt::Arguments, 131 | file: &'static str, line: u32) -> ! { 132 | kprintln!(r" 133 | Kernel Panic in file {} at Line {}: 134 | {}", file, line, msg); 135 | loop {} 136 | } 137 | -------------------------------------------------------------------------------- /filesystem/src/fat32/directory.rs: -------------------------------------------------------------------------------- 1 | // follow https://wiki.osdev.org/FAT#Directories 2 | use super::super::File; 3 | use alloc::string::String; 4 | 5 | bitflags! { 6 | pub struct FileAttributes: u8 { 7 | const READONLY = 0x01; 8 | const HIDDEN = 0x02; 9 | const SYSTEM = 0x04; 10 | const VOLUMEID = 0x08; 11 | const DIRECTORY = 0x10; 12 | const ARCHIVE = 0x20; 13 | const LONGFILENAME = Self::READONLY.bits | 14 | Self::HIDDEN.bits | Self::SYSTEM.bits | Self::VOLUMEID.bits; 15 | } 16 | } 17 | 18 | #[repr(packed, C)] 19 | #[derive(Debug, Copy, Clone)] 20 | pub struct LongFileName { 21 | order: u8, // The order of this entry in the sequence of long file name entries 22 | name_first: [u16; 5], // The first 5, 2-byte characters of this entry 23 | attributes: u8, // Attribute. Always equals 0x0F. (the long file name attribute) 24 | long_entry_type: u8, // Long entry type. Zero for name entries. 25 | checksum: u8, 26 | name_middle: [u16; 6], 27 | reserved: u16, // always zero 28 | name_final: [u16; 2], 29 | } 30 | 31 | impl LongFileName { 32 | pub fn get_name(&self) -> String { 33 | // long file name total 13 bytes 34 | let mut buff = vec![0u16; 13]; 35 | 36 | buff[..5].clone_from_slice(&self.name_first); 37 | buff[5..11].clone_from_slice(&self.name_middle); 38 | buff[11..].clone_from_slice(&self.name_final); 39 | 40 | let last_index = buff.len(); 41 | String::from_utf16_lossy(&buff[..last_index]) 42 | } 43 | } 44 | 45 | // Standard 8.3 format 46 | #[repr(packed, C)] 47 | #[derive(Debug, Copy, Clone)] 48 | pub struct FatDirectory { 49 | pub name: [u8; 11], 50 | attributes: u8, 51 | reserved_nt: u8, // Reserved for use by Windows NT. 52 | creation_time_precise: u8, // Creation time in tenths of a second 53 | creation_time: u16, // The time that the file was created. Multiply Seconds by 2.(Hour 5 bits | Minutes 6 bits | Seconds 5 bits) 54 | creation_date: u16, // The date on which the file was created.(Year 7 bits | Month 4 bits | Day 5 bits) 55 | last_accessed: u16, // Last accessed date. Same format as the creation date. 56 | first_cluster_high: u16, // The high 16 bits of this entry's first cluster number. 57 | last_modified_time: u16, // Last modification time. Same format as the creation time. 58 | last_modified_date: u16, // Last modification date. Same format as the creation date. 59 | first_cluster_low: u16, // The low 16 bits of this entry's first cluster number. 60 | file_size: u32, 61 | } 62 | 63 | impl FatDirectory { 64 | pub fn get_short_name(&self) -> String { 65 | use alloc::string::ToString; 66 | String::from_utf8(self.name.to_vec()) 67 | .expect("Invalid UTF-8.").trim().to_string() 68 | } 69 | 70 | pub fn get_cluster(&self) -> u32 { 71 | (self.first_cluster_high as u32) << 16 | self.first_cluster_low as u32 72 | } 73 | 74 | // is long file name 75 | pub fn is_lfn(&self) -> bool { 76 | self.attributes as u8 == FileAttributes::LONGFILENAME.bits 77 | } 78 | 79 | pub fn is_folder(&self) -> bool { 80 | let flag = FileAttributes::from_bits_truncate(self.attributes); 81 | flag.contains(FileAttributes::DIRECTORY) 82 | } 83 | } 84 | 85 | #[derive(Debug, Clone)] 86 | pub struct Directory { 87 | name: String, 88 | fat_directory: FatDirectory, 89 | } 90 | 91 | impl Directory { 92 | pub fn new(name: String, directory: FatDirectory) -> Self { 93 | Directory { 94 | name: name, 95 | fat_directory: directory, 96 | } 97 | } 98 | 99 | pub fn get_fat_dir(&self) -> &FatDirectory { 100 | &self.fat_directory 101 | } 102 | 103 | pub fn get_name(&self) -> String { 104 | use alloc::string::ToString; 105 | self.name.to_string() 106 | } 107 | 108 | #[allow(dead_code)] 109 | pub fn get_cluster(&self) -> u32 { 110 | self.fat_directory.get_cluster() 111 | } 112 | 113 | //pub fn is_folder(&self) -> bool { 114 | //let flag = FileAttributes::from_bits_truncate(self.fat_directory.attributes); 115 | //flag.contains(FileAttributes::Directory) 116 | //} 117 | } 118 | 119 | impl File for Directory { 120 | fn get_name(&self) -> String { 121 | use alloc::string::ToString; 122 | return self.name.to_string(); 123 | } 124 | 125 | fn get_size(&self) -> usize { 126 | self.fat_directory.file_size as usize 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/arch/x86_64/boot.asm: -------------------------------------------------------------------------------- 1 | global start 2 | 3 | extern long_mode_start 4 | 5 | section .bss 6 | align 4096 7 | p4_table: 8 | resb 4096 9 | p3_table: 10 | resb 4096 11 | p2_table: 12 | resb 4096 13 | stack_bottom: 14 | resb 4096 * 4 ; 16KB 15 | stack_top: 16 | 17 | 18 | section .rodata 19 | gdt64: 20 | dq 0 21 | .code: equ $ - gdt64 22 | dq (1<<41) | (1<<44) | (1<<47) | (1<<43) | (1<<53) 23 | .data: equ $ - gdt64 24 | dq (1<<44) | (1<<47) | (1<<41) 25 | .pointer: 26 | dw $ - gdt64 - 1 27 | dq gdt64 28 | 29 | section .text 30 | bits 32 31 | start: 32 | mov esp, stack_top 33 | mov edi, ebx 34 | 35 | call check_cpuid 36 | call check_long_mode 37 | 38 | call set_up_page_tables 39 | call enable_paging 40 | 41 | lgdt [gdt64.pointer] 42 | jmp gdt64.code:long_mode_start 43 | 44 | ; print `Hello` to screen 45 | mov WORD [0xb8000], 0x1f48 46 | mov WORD [0xb8002], 0x1f65 47 | mov WORD [0xb8004], 0x1f6c 48 | mov WORD [0xb8006], 0x1f6c 49 | mov WORD [0xb8008], 0x1f6f 50 | hlt 51 | 52 | error: 53 | ; print `ERR: code` 54 | mov WORD [0xb8000], 0x1f45 55 | mov WORD [0xb8002], 0x1f52 56 | mov WORD [0xb8004], 0x1f52 57 | mov WORD [0xb8006], 0x1f3a 58 | mov WORD [0xb8008], 0x1f20 59 | mov BYTE [0xb800A], al; 60 | hlt 61 | 62 | check_cpuid: 63 | ; Check if CPUID is supported by attempting to flip the ID bit (bit 21) in 64 | ; the FLAGS register. If we can flip it, CPUID is available. 65 | 66 | ; Copy FLAGS in to EAX via stack 67 | pushfd 68 | pop eax 69 | 70 | ; Copy to ECX as well for comparing later on 71 | mov ecx, eax 72 | 73 | ; Flip the ID bit 74 | xor eax, 1 << 21 75 | 76 | ; Copy EAX to FLAGS via the stack 77 | push eax 78 | popfd 79 | 80 | ; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) 81 | pushfd 82 | pop eax 83 | 84 | ; Restore FLAGS from the old version stored in ECX (i.e. flipping the ID bit 85 | ; back if it was ever flipped). 86 | push ecx 87 | popfd 88 | 89 | ; Compare EAX and ECX. If they are equal then that means the bit wasn't 90 | ; flipped, and CPUID isn't supported. 91 | xor eax, ecx 92 | jz .no_cpuid 93 | ret 94 | .no_cpuid: 95 | mov al, "1" 96 | jmp error 97 | 98 | check_long_mode: 99 | mov eax, 0x80000000 ; Set the A-register to 0x80000000. 100 | cpuid ; CPU identification. 101 | cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. 102 | jb .no_long_mode ; if it's less, the CPU is too old for long mode 103 | 104 | ; use extended info to test if long mode is available 105 | mov eax, 0x80000001 ; Set the A-register to 0x80000001. 106 | cpuid ; CPU identification. 107 | test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register. 108 | jz .no_long_mode ; If it's not set, there is no long mode 109 | ret 110 | .no_long_mode: 111 | mov al, "2" 112 | jmp error 113 | 114 | 115 | set_up_page_tables: 116 | ; recursive map P4 117 | mov eax, p4_table 118 | or eax, 0b11 ; present + writable 119 | mov [p4_table + 511 * 8], eax 120 | 121 | ; map first P4 entry to P3 table 122 | mov eax, p3_table 123 | or eax, 0b11 ; present + writable 124 | mov [p4_table], eax 125 | 126 | ; map first P3 entry to P2 table 127 | mov eax, p2_table 128 | or eax, 0b11 ; present + writable 129 | mov [p3_table], eax 130 | 131 | ; map each P2 entry to a huge 2MiB page 132 | mov ecx, 0 ; counter variable 133 | 134 | .map_p2_table: 135 | ; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx 136 | mov eax, 0x200000 ; 2MiB 137 | mul ecx ; start address of ecx-th page 138 | or eax, 0b10000011 ; present + writable + huge 139 | mov [p2_table + ecx * 8], eax ; map ecx-th entry 140 | 141 | inc ecx ; increase counter 142 | cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped 143 | jne .map_p2_table ; else map the next entry 144 | 145 | ret 146 | 147 | enable_paging: 148 | ; load P4 to cr3 register (cpu uses this to access the P4 table) 149 | mov eax, p4_table 150 | mov cr3, eax 151 | 152 | ; enable PAE-flag in cr4 (Physical Address Extension) 153 | mov eax, cr4 ; Set the A-register to control register 4. 154 | or eax, 1 << 5 ; Set the PAE-bit, which is the 6th bit (bit 5). 155 | mov cr4, eax ; Set control register 4 to the A-register. 156 | 157 | ; set the long mode bit in the EFER MSR (model specific register) 158 | mov ecx, 0xC0000080 ; Set the C-register to 0xC0000080, which is the EFER MSR. 159 | rdmsr ; Read from the model-specific register. 160 | or eax, 1 << 8 ; Set the LM-bit which is the 9th bit (bit 8). 161 | wrmsr ; Write to the model-specific register. 162 | 163 | ; enable paging in the cr0 register 164 | mov eax, cr0 ; Set the A-register to control register 0. 165 | or eax, 1 << 31 ; Set the PG-bit, which is the 32nd bit (bit 31). 166 | mov cr0, eax ; Set control register 0 to the A-register. 167 | 168 | ret 169 | -------------------------------------------------------------------------------- /device/src/ata.rs: -------------------------------------------------------------------------------- 1 | // follow https://wiki.osdev.org/ATA_PIO_Mode 2 | use core::slice; 3 | use io::{inb, outb, inw}; 4 | use disk::Disk; 5 | 6 | // An ATA bus typically has 9 I/O ports that control its behavior. 7 | // For the primary bus, these I/O ports are 0x1F0 through 0x1F7, and 0x3F6. 8 | // The values in this table are relative to the so-called I/O port base address. 9 | // So a port value of 1 actually means 0x1F0 + 1 = 0x1F1. 10 | // This is done because the base address may vary depending on the hardware. 11 | // 12 | // Port Offset Function Description 13 | // 0 Data Port Read/Write PIO data bytes on this port. 14 | // 1 Features / Error Information Usually used for ATAPI devices. 15 | // 2 Sector Count Number of sectors to read/write (0 is a special value). 16 | // 3 Sector Number / LBAlo This is CHS / LBA28 / LBA48 specific. 17 | // 4 Cylinder Low / LBAmid Partial Disk Sector address. 18 | // 5 Cylinder High / LBAhi Partial Disk Sector address. 19 | // 6 Drive / Head Port Used to select a drive and/or head. 20 | // 7 Command port / Regular Status port Used to send commands or read the current status. 21 | bitflags! { 22 | struct AtaBus: u16 { 23 | const DATA_PORT = 0x1F0; 24 | const ERROR_INFO = 0x1F1; 25 | const SECTOR_COUNT = 0x1F2; 26 | const LBA_LOW = 0x1F3; 27 | const LBA_MID = 0x1F4; 28 | const LBA_HIGH = 0x1F5; 29 | const DRIVE = 0x1F6; 30 | const COMMAND = 0x1F7; 31 | const STATUS = 0x3F6; 32 | } 33 | } 34 | 35 | const READ_SECTORS: u8 = 0x20; 36 | 37 | pub struct Ata {} 38 | 39 | impl Ata { 40 | unsafe fn poll(&self, condition: F) -> u8 where F: Fn(u8) -> bool { 41 | let mut reg_value: u8; 42 | loop { 43 | reg_value = inb(AtaBus::STATUS.bits); 44 | if condition(reg_value) { 45 | return reg_value; 46 | } 47 | } 48 | } 49 | } 50 | 51 | impl Disk for Ata{ 52 | // Send 0xE0 for the "master" or 0xF0 for the "slave", ORed with the highest 4 bits of the LBA to port 0x1F6: outb(0x1F6, 0xE0 | (slavebit << 4) | ((LBA >> 24) & 0x0F)) 53 | // Send a NULL byte to port 0x1F1, if you like (it is ignored and wastes lots of CPU time): outb(0x1F1, 0x00) 54 | // Send the sectorcount to port 0x1F2: outb(0x1F2, (unsigned char) count) 55 | // Send the low 8 bits of the LBA to port 0x1F3: outb(0x1F3, (unsigned char) LBA)) 56 | // Send the next 8 bits of the LBA to port 0x1F4: outb(0x1F4, (unsigned char)(LBA >> 8)) 57 | // Send the next 8 bits of the LBA to port 0x1F5: outb(0x1F5, (unsigned char)(LBA >> 16)) 58 | // Send the "READ SECTORS" command (0x20) to port 0x1F7: outb(0x1F7, 0x20) 59 | // Wait for an IRQ or poll. 60 | // Transfer 256 16-bit values, a uint16_t at a time, into your buffer from I/O port 0x1F0. (In assembler, REP INSW works well for this.) 61 | // Then loop back to waiting for the next IRQ (or poll again -- see next note) for each successive sector. 62 | unsafe fn read(&self, block: u64, buffer: &mut [u8]) -> Result { 63 | // check 64 | if buffer.len() == 0 { 65 | return Err("Size of buffer can't be 0."); 66 | } else if buffer.len() % 512 != 0 { 67 | return Err("Buffer size must be a multiplication of sector size."); 68 | } else if buffer.len() / 512 > 127 { 69 | return Err("Can only read 127 sectors at a time in LBA28 mode."); 70 | } 71 | 72 | let sector_count = (buffer.len() / 512) as u8; 73 | let command: u8 = 0xE0_u8 | ((block >> 24) & 0x0F) as u8 | (0x40) as u8; // bit 6 enabled for 28 bit LBA mode. 74 | outb(AtaBus::DRIVE.bits, command); 75 | outb(AtaBus::SECTOR_COUNT.bits, sector_count) ; 76 | outb(AtaBus::LBA_LOW.bits, block as u8); 77 | outb(AtaBus::LBA_MID.bits, (block >> 8) as u8); 78 | outb(AtaBus::LBA_HIGH.bits, (block >> 16) as u8); 79 | outb(AtaBus::COMMAND.bits, READ_SECTORS); 80 | 81 | for sector in 0..sector_count { 82 | // poll 83 | let status = self.poll( 84 | |x| (x & 0x80 == 0 && x & 0x8 != 0) || x & 0x1 != 0 || x & 0x20 != 0 85 | ); 86 | 87 | if status & 1 != 0 { 88 | if sector == 0 { 89 | return Err("No sectors read."); 90 | } 91 | // return amount of read sectors 92 | return Ok(sector); 93 | } else if status & 0x20 != 0 { 94 | return Err("Drive Fault occured."); 95 | } 96 | 97 | // Read data to buffer 98 | let buff = slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u16, buffer.len()/2); 99 | for i in 0..buff.len() { 100 | buff[i+(sector as usize*256)] = inw(AtaBus::DATA_PORT.bits); 101 | } 102 | 103 | // After transferring the last uint16_t of a PIO data block to the data IO port, 104 | // give the drive a 400ns delay to reset its DRQ bit 105 | for _ in 0..4 { 106 | inb(AtaBus::STATUS.bits); 107 | } 108 | } 109 | // return the amount of sectors read 110 | Ok(sector_count) 111 | } 112 | 113 | #[allow(unused_variables)] 114 | unsafe fn write_at(&self, block: u64, buffer: &[u8]) -> Result { 115 | unimplemented!(); 116 | } 117 | } 118 | 119 | pub const ATA: Ata = Ata {}; 120 | -------------------------------------------------------------------------------- /memory/src/paging/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::entry::*; 2 | use super::{PAGE_SIZE, Frame, FrameAllocator}; 3 | use self::temporary_page::TemporaryPage; 4 | pub use self::mapper::Mapper; 5 | use core::ops::{Add, Deref, DerefMut}; 6 | use multiboot2::BootInformation; 7 | 8 | mod entry; 9 | mod table; 10 | mod temporary_page; 11 | mod mapper; 12 | 13 | const ENTRY_COUNT: usize = 512; 14 | 15 | pub type PhysicalAddress = usize; 16 | pub type VirtualAddress = usize; 17 | 18 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 19 | pub struct Page { 20 | number: usize, 21 | } 22 | 23 | impl Page { 24 | pub fn containing_address(address: VirtualAddress) -> Page { 25 | assert!( 26 | address < 0x0000_8000_0000_0000 || address >= 0xffff_8000_0000_0000, 27 | "invalid address: 0x{:x}", 28 | address 29 | ); 30 | Page { number: address / PAGE_SIZE } 31 | } 32 | 33 | pub fn start_address(&self) -> usize { 34 | self.number * PAGE_SIZE 35 | } 36 | 37 | fn p4_index(&self) -> usize { 38 | (self.number >> 27) & 0o777 39 | } 40 | fn p3_index(&self) -> usize { 41 | (self.number >> 18) & 0o777 42 | } 43 | fn p2_index(&self) -> usize { 44 | (self.number >> 9) & 0o777 45 | } 46 | fn p1_index(&self) -> usize { 47 | (self.number >> 0) & 0o777 48 | } 49 | 50 | pub fn range_inclusive(start: Page, end: Page) -> PageIter { 51 | PageIter { 52 | start: start, 53 | end: end, 54 | } 55 | } 56 | } 57 | 58 | impl Add for Page { 59 | type Output = Page; 60 | 61 | fn add(self, rhs: usize) -> Page { 62 | Page { number: self.number + rhs } 63 | } 64 | } 65 | 66 | #[derive(Clone)] 67 | pub struct PageIter { 68 | start: Page, 69 | end: Page, 70 | } 71 | 72 | impl Iterator for PageIter { 73 | type Item = Page; 74 | 75 | fn next(&mut self) -> Option { 76 | if self.start <= self.end { 77 | let page = self.start; 78 | self.start.number += 1; 79 | Some(page) 80 | } else { 81 | None 82 | } 83 | } 84 | } 85 | 86 | pub struct ActivePageTable { 87 | mapper: Mapper, 88 | } 89 | 90 | impl Deref for ActivePageTable { 91 | type Target = Mapper; 92 | 93 | fn deref(&self) -> &Mapper { 94 | &self.mapper 95 | } 96 | } 97 | 98 | impl DerefMut for ActivePageTable { 99 | fn deref_mut(&mut self) -> &mut Mapper { 100 | &mut self.mapper 101 | } 102 | } 103 | 104 | impl ActivePageTable { 105 | unsafe fn new() -> ActivePageTable { 106 | ActivePageTable { mapper: Mapper::new() } 107 | } 108 | 109 | pub fn with( 110 | &mut self, 111 | table: &mut InactivePageTable, 112 | temporary_page: &mut temporary_page::TemporaryPage, // new 113 | f: F, 114 | ) where 115 | F: FnOnce(&mut Mapper), 116 | { 117 | use x86_64::registers::control_regs; 118 | use x86_64::instructions::tlb; 119 | 120 | { 121 | let backup = Frame::containing_address(control_regs::cr3().0 as usize); 122 | 123 | // map temporary_page to current p4 table 124 | let p4_table = temporary_page.map_table_frame(backup.clone(), self); 125 | 126 | // overwrite recursive mapping 127 | self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE); 128 | tlb::flush_all(); 129 | 130 | // execute f in the new context 131 | f(self); 132 | 133 | // restore recursive mapping to original p4 table 134 | p4_table[511].set(backup, PRESENT | WRITABLE); 135 | tlb::flush_all(); 136 | } 137 | 138 | temporary_page.unmap(self); 139 | } 140 | 141 | pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable { 142 | use x86_64::PhysicalAddress; 143 | use x86_64::registers::control_regs; 144 | 145 | let old_table = InactivePageTable { 146 | p4_frame: Frame::containing_address(control_regs::cr3().0 as usize), 147 | }; 148 | unsafe { 149 | control_regs::cr3_write(PhysicalAddress(new_table.p4_frame.start_address() as u64)); 150 | } 151 | old_table 152 | } 153 | } 154 | 155 | pub struct InactivePageTable { 156 | p4_frame: Frame, 157 | } 158 | 159 | impl InactivePageTable { 160 | pub fn new( 161 | frame: Frame, 162 | active_table: &mut ActivePageTable, 163 | temporary_page: &mut TemporaryPage, 164 | ) -> InactivePageTable { 165 | { 166 | let table = temporary_page.map_table_frame(frame.clone(), active_table); 167 | table.zero(); 168 | table[511].set(frame.clone(), PRESENT | WRITABLE); 169 | } 170 | temporary_page.unmap(active_table); 171 | 172 | InactivePageTable { p4_frame: frame } 173 | } 174 | } 175 | 176 | pub fn remap_the_kernel(allocator: &mut A, boot_info: &BootInformation) -> ActivePageTable 177 | where 178 | A: FrameAllocator, 179 | { 180 | let mut temporary_page = TemporaryPage::new(Page { number: 0xcafebabe }, allocator); 181 | 182 | let mut active_table = unsafe { ActivePageTable::new() }; 183 | let mut new_table = { 184 | let frame = allocator.allocate_frame().expect("no more frames"); 185 | InactivePageTable::new(frame, &mut active_table, &mut temporary_page) 186 | }; 187 | 188 | active_table.with(&mut new_table, &mut temporary_page, |mapper| { 189 | let elf_sections_tag = boot_info 190 | .elf_sections_tag() 191 | .expect("Memory map tag required"); 192 | 193 | // identity map the allocated kernel sections 194 | for section in elf_sections_tag.sections() { 195 | if !section.is_allocated() { 196 | // section is not loaded to memory 197 | continue; 198 | } 199 | 200 | assert!( 201 | section.addr as usize % PAGE_SIZE == 0, 202 | "sections need to be page aligned" 203 | ); 204 | kprintln!( 205 | "mapping section at addr: {:#x}, size: {:#x}", 206 | section.addr, 207 | section.size 208 | ); 209 | 210 | let flags = EntryFlags::from_elf_section_flags(section); 211 | 212 | let start_frame = Frame::containing_address(section.start_address()); 213 | let end_frame = Frame::containing_address(section.end_address() - 1); 214 | for frame in Frame::range_inclusive(start_frame, end_frame) { 215 | mapper.identity_map(frame, flags, allocator); 216 | } 217 | } 218 | 219 | // identity map the VGA text buffer 220 | let vga_buffer_frame = Frame::containing_address(0xb8000); 221 | mapper.identity_map(vga_buffer_frame, WRITABLE, allocator); 222 | 223 | // identity map the multiboot info structure 224 | let multiboot_start = Frame::containing_address(boot_info.start_address()); 225 | let multiboot_end = Frame::containing_address(boot_info.end_address() - 1); 226 | for frame in Frame::range_inclusive(multiboot_start, multiboot_end) { 227 | mapper.identity_map(frame, PRESENT, allocator); 228 | } 229 | }); 230 | 231 | let old_table = active_table.switch(new_table); 232 | kprintln!("NEW TABLE!!!"); 233 | 234 | let old_p4_page = Page::containing_address(old_table.p4_frame.start_address()); 235 | active_table.unmap(old_p4_page, allocator); 236 | kprintln!("guard page at {:#x}", old_p4_page.start_address()); 237 | 238 | active_table 239 | } 240 | -------------------------------------------------------------------------------- /filesystem/src/fat32/mod.rs: -------------------------------------------------------------------------------- 1 | mod bpb; 2 | mod directory; 3 | mod cluster; 4 | 5 | use core::slice; 6 | use core::str::Split; 7 | use alloc::Vec; 8 | use alloc::string::String; 9 | 10 | use self::directory::{Directory, LongFileName, FatDirectory}; 11 | use self::bpb::{Ebpb}; 12 | use self::cluster::{Cluster, ClusterChain}; 13 | use super::{File, FilePointer, FileSystem}; 14 | use device::disk::Disk; 15 | 16 | // TODO some img file START_SECTOR is not zero 17 | const START_SECTOR: u64 = 0; 18 | 19 | pub struct Fat32 { 20 | pub ebpb: Ebpb, 21 | } 22 | 23 | // transfer C code from os-dev wiki https://wiki.osdev.org/FAT#Programming_Guide 24 | impl Fat32 { 25 | pub unsafe fn new(disk: &Disk) -> Self { 26 | let mut boot_record = [0u8; 512]; 27 | disk.read(START_SECTOR, &mut boot_record) 28 | .expect("EBPB Read Error"); 29 | let ebpb = *(boot_record.as_ptr() as *const Ebpb); 30 | Fat32 { 31 | ebpb: ebpb, 32 | } 33 | } 34 | 35 | #[inline] 36 | fn get_fat_size(&self) -> u32 { 37 | if self.ebpb.bpb.sectors_per_fat == 0 { 38 | return self.ebpb.sectors_per_fat; 39 | } 40 | self.ebpb.bpb.sectors_per_fat as u32 41 | } 42 | 43 | // always be zero in FAT32 44 | #[inline] 45 | fn get_root_dir_sector(&self) -> u32 { 46 | //((self.ebpb.bpb.root_entry_count * 32) + (self.ebpb.bpb.bytes_per_sector - 1)) / self.ebpb.bpb.bytes_per_sector 47 | 0 48 | } 49 | 50 | #[inline] 51 | fn get_first_data_sector(&self) -> u64 { 52 | self.ebpb.bpb.reserved_sectors_count as u64 + 53 | (self.ebpb.bpb.table_count as u32 * self.get_fat_size()) as u64 + 54 | self.get_root_dir_sector() as u64 55 | } 56 | 57 | #[inline] 58 | fn get_bytes_in_cluster(&self) -> u32 { 59 | self.ebpb.bpb.sectors_per_cluster as u32 * self.ebpb.bpb.bytes_per_sector as u32 60 | } 61 | 62 | #[inline] 63 | fn first_sector_of_cluster(&self, cluster: u32) -> u64 { 64 | self.get_first_data_sector() + ((cluster-2) * (self.ebpb.bpb.sectors_per_cluster as u32)) as u64 65 | } 66 | 67 | #[inline] 68 | fn get_total_clusters(&self) -> u32 { 69 | let data_sectors = self.ebpb.bpb.total_sectors_large as usize - (self.ebpb.bpb.reserved_sectors_count as usize + self.ebpb.bpb.table_count as usize *32); 70 | data_sectors as u32 / self.ebpb.bpb.sectors_per_cluster as u32 71 | } 72 | 73 | // see https://wiki.osdev.org/FAT#Programming_Guide、0 74 | fn read_directories_from_cluster(&self, drive: &Disk, cluster: Cluster, directories: &mut Vec) { 75 | let mut temp_name: Option = None; 76 | let mut buffer = vec![0u8; self.get_bytes_in_cluster() as usize]; 77 | 78 | let sector = self.first_sector_of_cluster(cluster); 79 | kprintln!("sector {:?}", sector); 80 | let sectors_read = unsafe { 81 | drive.read(sector, &mut buffer) 82 | }.expect("Disk Read Error") as usize; 83 | //kprintln!("{:?}", &buffer[0..3]); 84 | 85 | //for i in 0..1000000000{ 86 | //if i % 512 != 0 { 87 | //continue; 88 | //} 89 | //let mut buffer = vec![0u8; self.get_bytes_in_cluster() as usize]; 90 | //let sectors_read = unsafe { 91 | //drive.read(i, &mut buffer) 92 | //}.expect("Error reading from disk.") as usize; 93 | //if buffer[0] == 0 { 94 | //continue; 95 | //} 96 | //kprintln!("{} {:?} ", i, &buffer[0..3]); 97 | //} 98 | //panic!(); 99 | let directories_slice = unsafe { 100 | slice::from_raw_parts(buffer.as_ptr() as *const FatDirectory, 101 | (sectors_read * self.ebpb.bpb.bytes_per_sector as usize / 32) as usize) 102 | }; 103 | kprintln!("len {:?}", directories_slice.len()); 104 | //kprintln!("slice {:?}", directories_slice); 105 | 106 | for directory in directories_slice { 107 | // If the first byte of the directory entry is 0, there are no more directories. 108 | if directory.name[0] == 0 { 109 | break; 110 | } 111 | // If the first byte of the entry is equal to 0xE5 then the entry is unused. 112 | if directory.name[0] == 0xE5 { 113 | continue; 114 | } 115 | 116 | //kprintln!("name {:?}", directory.name); 117 | 118 | if directory.is_lfn() { 119 | let lfn_directory = unsafe { 120 | *(directory as *const _ as *const LongFileName) 121 | }; 122 | let long_file_name = lfn_directory.get_name(); 123 | if temp_name != None { 124 | // If a long file name is in the buffer and the current directory is another long file name, 125 | // apply it to the previously stored file name. 126 | temp_name = Some(format!("{}{}", long_file_name, temp_name.unwrap())); 127 | } else { 128 | temp_name = Some(long_file_name); 129 | } 130 | } else { 131 | if let Some(stored_name) = temp_name { 132 | directories.push(Directory::new(stored_name, *directory)); 133 | temp_name = None; 134 | } else { 135 | directories.push(Directory::new(directory.get_short_name(), *directory)); 136 | } 137 | } 138 | } 139 | } 140 | 141 | fn read_cluster_chain(&self, drive: &Disk, first_cluster: u32, directories: &mut Vec) { 142 | let cluster_chain = ClusterChain::new(first_cluster, self, drive); 143 | for cluster in cluster_chain { 144 | self.read_directories_from_cluster(drive, cluster, directories); 145 | } 146 | } 147 | 148 | fn read_folder(&self, drive: &Disk, cluster: u32) -> Vec { 149 | let mut directories: Vec = Vec::new(); 150 | self.read_cluster_chain(drive, cluster, &mut directories); 151 | directories 152 | } 153 | 154 | fn find_file(&self, drive: &Disk, cluster: u32, path: &mut Split<&str>) -> Option { 155 | if let Some(part) = path.next() { 156 | let current_dirs = self.read_folder(drive, cluster); 157 | //kprintln!("current_dirs: {:?}", current_dirs); 158 | //kprintln!("path part : {:?}", part); 159 | let dir: Directory = current_dirs 160 | .iter() 161 | .find(|dir| dir.get_name() == part) 162 | .expect(&format!("Folder {} not found.", part)) 163 | .clone(); 164 | if dir.get_fat_dir().is_folder() { 165 | return self.find_file(drive, dir.get_fat_dir().get_cluster(), path); 166 | } 167 | return Some(dir); 168 | } 169 | None 170 | } 171 | } 172 | 173 | impl FileSystem for Fat32 { 174 | type FileType = Directory; 175 | 176 | fn open_file(&self, drive: &Disk, file_name: &str) -> Option> { 177 | let mut path_pattern = file_name.split("/"); 178 | if let Some(file) = self.find_file(drive, self.ebpb.root_dir_cluster, &mut path_pattern) { 179 | return Some(FilePointer::new(file, 0)); 180 | } 181 | None 182 | } 183 | 184 | fn read_file(&self, drive: &Disk, file_pointer: &FilePointer, buffer: &mut [u8]) -> Option { 185 | let read_length = buffer.len(); 186 | let file = file_pointer.get_file(); 187 | let file_size = file.get_size(); 188 | let read_start = file_pointer.get_pos(); 189 | let cluster_size = self.get_bytes_in_cluster() as usize; 190 | 191 | if read_length % cluster_size == 0 { 192 | let starting_cluster = file.get_fat_dir().get_cluster(); 193 | let mut cluster_chain = ClusterChain::new(starting_cluster, self, drive); 194 | let current_cluster_index = read_start/cluster_size; 195 | 196 | if let Some(mut current_cluster) = cluster_chain.nth(current_cluster_index) { 197 | let mut part = 0; 198 | while (read_start + part*cluster_size < file_size) || (part*cluster_size < read_length) { 199 | let mut temp_buffer = vec![0u8; cluster_size]; 200 | unsafe { 201 | let _ = drive.read(self.first_sector_of_cluster(current_cluster), &mut temp_buffer); 202 | } 203 | 204 | buffer[part*cluster_size..(part+1)*cluster_size].clone_from_slice(&temp_buffer); 205 | part += 1; 206 | if let Some(next_cluster) = cluster_chain.next() { 207 | current_cluster = next_cluster; 208 | } else { 209 | break; 210 | } 211 | } 212 | // TODO 213 | return Some(file_size - read_start); 214 | } 215 | } 216 | None 217 | } 218 | } 219 | --------------------------------------------------------------------------------