├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── _config.yml ├── _posts ├── 2018-6-4-A stack trace for your OS.md ├── 2018-6-4-Entering user mode in long mode.md └── 2018-6-5-Context switching and preemptive multitasking [0].md ├── assets ├── context_switch │ ├── after_switch.png │ └── before_switch.png ├── stack_trace │ └── stack_on_call.png └── user_mode │ └── exception_frame.png ├── bochsrc ├── boot_disk └── test.txt ├── grub.cfg ├── index.md └── kernel ├── CMakeLists.txt ├── Cargo.toml ├── Xargo.toml ├── assembly ├── boot_checks.asm ├── boot_entry.asm ├── boot_paging.asm ├── constants.inc └── multiboot_header.asm ├── linker.ld ├── rust-toolchain ├── src ├── debug │ ├── mod.rs │ ├── stack_trace.rs │ └── symbols.rs ├── display │ ├── mod.rs │ └── text_mode │ │ ├── buffer.rs │ │ ├── buffers │ │ ├── boot_buffer.rs │ │ ├── mod.rs │ │ └── window_buffer.rs │ │ ├── drivers │ │ ├── global_facade.rs │ │ ├── mod.rs │ │ └── vga_text_driver.rs │ │ ├── functions.rs │ │ ├── low_depth_colour.rs │ │ ├── mod.rs │ │ ├── position.rs │ │ ├── printer.rs │ │ ├── printers │ │ ├── list_printer.rs │ │ ├── mod.rs │ │ ├── scroll_printer.rs │ │ └── selectable_list.rs │ │ ├── text_display.rs │ │ ├── utility │ │ ├── boot_status.rs │ │ └── mod.rs │ │ └── writers │ │ ├── debug_writer.rs │ │ ├── default_writer.rs │ │ ├── error_writer.rs │ │ └── mod.rs ├── graph │ ├── functions.rs │ ├── location.rs │ ├── mod.rs │ ├── provider.rs │ ├── providers │ │ ├── memory_disk.rs │ │ ├── mod.rs │ │ └── root.rs │ ├── resource.rs │ └── resources │ │ ├── memory_file.rs │ │ └── mod.rs ├── interrupts │ ├── functions.rs │ ├── gdt.rs │ ├── gdt_descriptor.rs │ ├── handlers.rs │ ├── mod.rs │ ├── pic_functions.rs │ └── pit_functions.rs ├── keyboard │ ├── drivers │ │ ├── mod.rs │ │ └── ps2_driver.rs │ ├── functions.rs │ ├── key_event.rs │ ├── key_other.rs │ ├── key_printable.rs │ └── mod.rs ├── lib.rs ├── memory │ ├── address.rs │ ├── frame.rs │ ├── frame_allocator.rs │ ├── frame_allocators │ │ ├── huge_frame_divider.rs │ │ ├── mod.rs │ │ └── tiny_allocator.rs │ ├── functions.rs │ ├── generic_allocators │ │ ├── boot_allocator.rs │ │ ├── fixed_frame_recycler.rs │ │ ├── frame_recycler.rs │ │ ├── global_frame_allocator.rs │ │ ├── mod.rs │ │ └── post_boot_allocator.rs │ ├── huge_frame_allocators │ │ ├── huge_boot_bump_allocator.rs │ │ ├── huge_bump_allocator.rs │ │ └── mod.rs │ ├── memory_area.rs │ └── mod.rs ├── paging │ ├── active_page_table.rs │ ├── address.rs │ ├── functions.rs │ ├── inactive_page_table.rs │ ├── mod.rs │ ├── page.rs │ ├── page_entry.rs │ ├── page_iter.rs │ ├── page_mapper.rs │ ├── page_table.rs │ ├── reserved.rs │ ├── table_level.rs │ └── temporary_page.rs ├── shell │ ├── evaluator.rs │ ├── evaluators │ │ ├── memory.rs │ │ ├── memory_test.rs │ │ ├── mod.rs │ │ └── root.rs │ ├── functions.rs │ ├── kernel_shell.rs │ ├── mod.rs │ ├── process.rs │ └── shell_display.rs ├── structures │ ├── fixed_stack.rs │ ├── frame_store.rs │ └── mod.rs ├── system_call │ ├── functions.rs │ └── mod.rs ├── task │ ├── functions.rs │ ├── loaders │ │ ├── flat_binary.rs │ │ ├── functions.rs │ │ ├── mod.rs │ │ └── stack.rs │ ├── mod.rs │ ├── scheduler.rs │ ├── schedulers │ │ ├── mod.rs │ │ └── round_robin.rs │ └── thread.rs ├── tests │ ├── display │ │ ├── mod.rs │ │ └── text_mode │ │ │ ├── buffer.rs │ │ │ ├── display_facade.rs │ │ │ ├── dummy_display.rs │ │ │ ├── mod.rs │ │ │ └── scroll_printer.rs │ ├── memory │ │ ├── fixed_frame_recycler.rs │ │ ├── memory_area.rs │ │ └── mod.rs │ ├── mod.rs │ ├── structures │ │ ├── frame_store.rs │ │ └── mod.rs │ └── utility │ │ ├── math.rs │ │ └── mod.rs └── utility │ ├── convert.rs │ ├── global.rs │ ├── math.rs │ ├── mod.rs │ ├── multiboot_structure.rs │ └── pseudo_random.rs └── x86_64-example_os.json /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | .idea/codeStyles/ 3 | .idea/dictionaries/ 4 | .idea/misc.xml 5 | .idea/modules.xml 6 | .idea/example_os.iml 7 | .idea/workspace.xml 8 | cmake-build-debug/ 9 | cmake-build-release/ 10 | kernel/target/ 11 | .idea/inspectionProfiles/ 12 | .DS_Store 13 | .idea/openbox_os.iml 14 | .idea/vcs.xml 15 | Gemfile 16 | Gemfile.lock 17 | _site/ 18 | boot_disk/kernel/ 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | add_subdirectory(kernel) 4 | 5 | set(BUILD_TARGET x86_64-example_os) 6 | set(KERNEL_BINARY "kernel.bin") 7 | 8 | set(BOOTABLE_ISO ${BUILD_TARGET}.iso) 9 | set(ISO_FILES_DIR ${CMAKE_CURRENT_BINARY_DIR}/iso_files) 10 | set(GRUB_CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/grub.cfg) 11 | set(GRUB_BOOT_TOOL /usr/local/bin/grub-mkrescue) 12 | 13 | set(TAR_TOOL tar) 14 | set(BOOT_DISK_ARCHIVE ${ISO_FILES_DIR}/boot/boot_disk.tar) 15 | set(BOOT_DISK ${CMAKE_CURRENT_SOURCE_DIR}/boot_disk) 16 | 17 | set(SYMBOL_STRIP_TOOL nm) 18 | set(OBJECT_COPY_TOOL gobjcopy) 19 | set(TEXT_ONLY_BINARY ${CMAKE_CURRENT_BINARY_DIR}/kernel/stripped_kernel.bin) 20 | set(SYMBOL_TABLE ${BOOT_DISK}/kernel/symbols.table) 21 | 22 | add_custom_target(${BOOTABLE_ISO}) 23 | add_dependencies(${BOOTABLE_ISO} ${KERNEL_BINARY}) 24 | configure_file(${GRUB_CONFIG} ${ISO_FILES_DIR}/boot/grub/grub.cfg) 25 | add_custom_command(TARGET ${BOOTABLE_ISO} PRE_BUILD 26 | COMMAND ${CMAKE_COMMAND} -E make_directory ${ISO_FILES_DIR}/boot 27 | COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/boot_disk/kernel 28 | 29 | COMMAND ${CMAKE_COMMAND} -E copy $ ${ISO_FILES_DIR}/boot/kernel.bin 30 | 31 | COMMAND ${CMAKE_COMMAND} -E remove ${TEXT_ONLY_BINARY} 32 | COMMAND ${OBJECT_COPY_TOOL} --only-section=.text $ ${TEXT_ONLY_BINARY} 2> /dev/null 33 | COMMAND ${SYMBOL_STRIP_TOOL} ${TEXT_ONLY_BINARY} > ${SYMBOL_TABLE} 34 | 35 | COMMAND ${TAR_TOOL} cf ${BOOT_DISK_ARCHIVE} -C ${BOOT_DISK} . 36 | COMMAND ${GRUB_BOOT_TOOL} -o ${CMAKE_CURRENT_BINARY_DIR}/${BOOTABLE_ISO} ${ISO_FILES_DIR} 2> /dev/null 37 | COMMAND ${CMAKE_COMMAND} -E remove_directory ${ISO_FILES_DIR}) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Technocoder 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 | # example_os 2 | A heavily commented OS in Rust for reference purposes (documentation in progress). 3 | Check out the companion site here: [Tutorials](https://techno-coder.github.io/example_os/) 4 | 5 | This OS is a hard fork of my private OS that I'm also working on. No more features will be added to this OS as it is for reference purposes only. 6 | Huge thanks to [Phil-opp](https://os.phil-opp.com) and the [OSDev wiki](https://wiki.osdev.org) 7 | 8 | ## License 9 | Everything in this repository is under the MIT license, excluding the _posts and assets folder (which contains data for the companion website), which is copyrighted. 10 | 11 | ## Features 12 | - Preemptive Multitasking (including system calls) 13 | - System Calls (well, one of them) 14 | - Primitive filesystem (Tar archive ram disk) 15 | - Stack traces (with kernel symbols) 16 | - Huge page support 17 | - Huge frame allocation 18 | - Keyboard driver 19 | - Kernel shell (with tab completion) 20 | 21 | ## Prerequisites 22 | You can find most of the prerequisites needed in the `CMakeLists.txt` files located in the root and in `kernel` folder. 23 | 24 | Build requirements: 25 | - NASM Assembler 26 | - Binutils 27 | - nm (Symbol table displayer) 28 | - Rust 29 | - Cargo 30 | - Xargo 31 | - LLVM linker 32 | - tar (for creating the boot disk) 33 | - grub-mkrescue (to generate the .iso file) 34 | - CMake (if you wish to compile with CMake) 35 | 36 | Execution requirements: 37 | - QEMU 38 | - Bochs 39 | 40 | A `bochsrc` file is located in the root. 41 | 42 | ## Build instructions 43 | Once you have installed the prerequisites, you can either compile everything manually or use CMake. 44 | 45 | ### CMake 46 | Change `XARGO_TOOL` in the kernel CMake file from `xargo-m` to `xargo`. The -m suffix is to avoid Intellij-Rust from using Xargo to run tests. 47 | Change `OBJECT_COPY_TOOL` from `gobjcopy` to `objcopy` or whatever the `objcopy` tool is aliased to. 48 | 49 | Finally, run this command: 50 | `cmake --build cmake-build-debug --target x86_64-example_os.iso` 51 | The .iso file will be located in the cmake-build-debug folder 52 | 53 | ### CLion 54 | Import the project and then build the `.iso` target after making the changes in the CMake section. 55 | 56 | ### Manually 57 | 1. Use NASM to compile all the files in `kernel/assembly` with the command 58 | `nasm -felf64 -w-number-overflow -Ikernel/assembly` 59 | 2. Use Xargo to compile the Rust binary with the command (execute in the `kernel` folder) 60 | ``RUST_FLAGS=-Cforce-frame-pointers=yes RUST_TARGET_PATH=`pwd` xargo build --target x86_64-example_os`` 61 | The `RUST_TARGET_PATH` is needed to allow xargo to locate the target specification. 62 | 3. Link all the files together 63 | `ld.lld --gc-sections -Tlinker.ld ` 64 | The Rust file is located in `kernel/target/x86_64-example_os/debug/libkernel.a` 65 | 4. Create a `boot_disk` folder, and a `kernel` folder inside of that 66 | 5. Use `nm` against the linked binary and pipe it to a file `symbols.table`. 67 | Copy this file into the `boot_disk/kernel` folder. 68 | 6. Tar the `boot_disk` folder into `boot_disk.tar` 69 | 7. Create a `iso_files` folder, and a `boot` folder inside of that, and a `grub` folder inside of that 70 | 8. Copy the `grub.cfg` file into `iso_files/boot/grub` 71 | 9. Copy the `boot_disk.tar` into `iso_files/boot` 72 | 10. Create the .iso with the command 73 | `grub-mkrescue -o iso_files` 74 | 75 | ## Execution 76 | ### Bochs 77 | Just run `bochs` in the root directory. You may need to change the path to the iso file in the `bochsrc`. 78 | ### QEMU 79 | Run the command 80 | `qemu-system-x86_64 -cdrom x86_64-example_os.iso -monitor stdio -d int -no-reboot -no-shutdown` 81 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: exampleOS 2 | description: exampleOS is an OS for reference purposes 3 | author: Technocoder 4 | 5 | url: https://techno-coder.github.io 6 | baseurl: /example_os 7 | 8 | google_analytics: UA-120286070-1 9 | theme: jekyll-theme-hacker 10 | plugins: 11 | - jekyll-feed 12 | - jekyll-sitemap 13 | - jekyll-seo-tag 14 | -------------------------------------------------------------------------------- /_posts/2018-6-4-A stack trace for your OS.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A stack trace for your OS 3 | description: Implementation details for creating a stack tracer 4 | tags: stack-trace frame-pointer debugging 5 | --- 6 | ## What? 7 | A stack trace displays the location of every function call, 8 | leading up to when the stack trace itself is called. Essentially, it 9 | tells you the path your code took. 10 | 11 | ## Why? 12 | A stack trace is incredibly helpful for debugging purposes as 13 | there can be multiple paths to a function call. It is important 14 | to determine which path caused a function call. 15 | 16 | ## Where? 17 | In exampleOS we automatically print a stack trace whenever the 18 | kernel panics. 19 | 20 | ## How? 21 | There are two ways debuggers print a stack trace. They can either 22 | parse the [DWARF](http://www.dwarfstd.org) debugging format which 23 | is produced by the compiler, or they can "walk the stack" by 24 | following the stack base pointers. exampleOS uses the latter method 25 | as it is far simpler and works without loading the kernel 26 | symbols. This allows us to produce a stack trace even in the very 27 | early stages of the kernel booting process. 28 | 29 | ## The call instruction 30 | Functions are called using the assembly call instruction: 31 | ```asm 32 | call a_function 33 | ``` 34 | `a_function` is replaced by the address of the the function during 35 | the linking stage. More importantly, is how the call instruction works. 36 | 37 | When the processor reaches the call instruction, it saves the current 38 | instruction pointer onto the stack. This is called a "return address". 39 | This is so it knows where to continue once the called function has returned. 40 | We use this later to print the location of the function call. When 41 | the called function returns, the processor examines the stack, 42 | pops off the return address, and jumps to the return address. 43 | 44 | ## The frame pointer 45 | Compilers have an option to add frame pointers to every function. When 46 | frame pointers are enabled, two important assembly instructions are 47 | inserted at the start of every function: 48 | ```asm 49 | push rbp 50 | mov rbp, rsp 51 | ``` 52 | This is what the stack looks like when a function is called: 53 | 54 | 55 | 56 | From the diagram, we can see that the return address is always above the 57 | address stored in `rbp`. So, in order to find the last function's 58 | return address, we just add 8 (because registers have a size of 8 bytes in 59 | long mode) from the address stored in `rbp`. 60 | 61 | ## Enabling frame pointers 62 | The Rust compiler has a flag to enable frame pointers: 63 | `RUSTFLAGS=-Cforce-frame-pointers=yes` 64 | This is referenced in the compiler source [here](https://github.com/rust-lang/rust/blob/01a9b30c332810ad0c570e8fed91f956417dec3a/src/librustc/session/mod.rs#L667). 65 | 66 | For C and C++ compilers, the search term is `no-omit-frame-pointer`. 67 | 68 | ## The code 69 | You can see exampleOS's full implementation of a stack tracer [here](https://github.com/Techno-coder/example_os/blob/master/kernel/src/debug/stack_trace.rs). 70 | 71 | First we have to obtain the contents of the `rbp` register: 72 | ```rust 73 | let mut base_pointer: *const usize; 74 | unsafe { asm!("mov rax, rbp" : "={rax}"(base_pointer) ::: "intel") } 75 | ``` 76 | 77 | Then, to get the return address, we increment the pointer and dereference it: 78 | ```rust 79 | let return_address = unsafe { *(base_pointer.offset(1)) } as usize; 80 | println!("Call site: {}", return_address); 81 | ``` 82 | We increment by 1 because `offset` is based on the size of the data type (`usize`, 83 | which has a size of 8 bytes) not the number of bytes. Note that in Rust, dereferencing 84 | a raw pointer is considered unsafe. 85 | 86 | Finally, we need to "walk the stack" and get the return addresses of all 87 | the previous stack frames. To do this, we set the current 88 | base pointer to the previous stack frame's base pointer: 89 | ```rust 90 | base_pointer = unsafe { (*base_pointer) as *const usize }; 91 | ``` 92 | and then we just repeat all the previous code, giving us a nice loop: 93 | ```rust 94 | let mut base_pointer: *const usize; 95 | unsafe { asm!("mov rax, rbp" : "={rax}"(base_pointer) ::: "intel") } 96 | loop { 97 | let return_address = unsafe { *(base_pointer.offset(1)) } as usize; 98 | println!("Call site: {}", return_address); 99 | base_pointer = unsafe { (*base_pointer) as *const usize }; 100 | } 101 | ``` 102 | 103 | ### Isn't this an infinite loop? 104 | That's right, this loop is going to continue forever, until it dereferences an 105 | invalid memory address (and makes the processor throw a fault). This is 106 | because we don't know when the stack ends. There's a simple solution to this: 107 | before we enter the kernel function, set the base pointer to zero, and then 108 | stop looping when reaching a base pointer that is equal to zero. 109 | 110 | Here's the relevant file in exampleOS: [boot_entry.asm](https://github.com/Techno-coder/example_os/blob/15a208f51768b3765154d59225f4a6427a22d0ce/kernel/assembly/boot_entry.asm#L72), 111 | specifically, this line: 112 | 113 | ```asm 114 | xor rbp, rbp 115 | ``` 116 | Now when the `boot_entry` function is called, a value of 0 will be pushed 117 | onto the stack. All we have to do is update our loop: 118 | ```rust 119 | // ... 120 | while !base_pointer.is_null() { 121 | // ... 122 | } 123 | ``` 124 | 125 | Put everything into its own function and then you're done! 126 | 127 | ## Final thoughts 128 | For now, you'll only see numbers in the stack trace. You can use `objdump` to 129 | map these numbers to the assembly code of your kernel. Later on, we'll be able 130 | to load the kernel symbol table and then print out function names. exampleOS's 131 | stack trace implementation does this. 132 | 133 | [Back to the root](https://techno-coder.github.io/example_os/) -------------------------------------------------------------------------------- /assets/context_switch/after_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techno-coder/example_os/459cd9d4baa229b72cdedfe29c6d3cbab5acd55e/assets/context_switch/after_switch.png -------------------------------------------------------------------------------- /assets/context_switch/before_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techno-coder/example_os/459cd9d4baa229b72cdedfe29c6d3cbab5acd55e/assets/context_switch/before_switch.png -------------------------------------------------------------------------------- /assets/stack_trace/stack_on_call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techno-coder/example_os/459cd9d4baa229b72cdedfe29c6d3cbab5acd55e/assets/stack_trace/stack_on_call.png -------------------------------------------------------------------------------- /assets/user_mode/exception_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Techno-coder/example_os/459cd9d4baa229b72cdedfe29c6d3cbab5acd55e/assets/user_mode/exception_frame.png -------------------------------------------------------------------------------- /bochsrc: -------------------------------------------------------------------------------- 1 | ata0-slave: type=cdrom, path=cmake-build-debug/x86_64-example_os.iso, status=inserted 2 | cpu: ips=100000000 3 | boot: cdrom 4 | magic_break: enabled=1 5 | -------------------------------------------------------------------------------- /boot_disk/test.txt: -------------------------------------------------------------------------------- 1 | Amazing, I'm a text file! 2 | If you're reading this, it means module loading 3 | is working perfectly. Thank you and have a 4 | nice day! 5 | -------------------------------------------------------------------------------- /grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "exampleOS" { 5 | multiboot2 /boot/kernel.bin 6 | module2 /boot/boot_disk.tar boot_disk 7 | boot 8 | } -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | ## Tutorials 4 |
    5 | {% for post in site.posts %} 6 | {{post.title}} 7 |
    8 | {% endfor %} 9 |
10 | 11 | An Atom/RSS feed is available [here](https://techno-coder.github.io/example_os/feed.xml) 12 | 13 | ## Got Feedback? 14 | Feel free to submit an issue to the [exampleOS repository](https://github.com/Techno-coder/example_os) 15 | with the label `feedback` 16 | 17 | ## About me 18 | [Github profile](https://github.com/Techno-coder) 19 | -------------------------------------------------------------------------------- /kernel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | enable_language(ASM_NASM) 3 | 4 | project(kernel) 5 | 6 | set(BUILD_TARGET x86_64-example_os) 7 | set(KERNEL_BINARY "${PROJECT_NAME}.bin") 8 | set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) 9 | 10 | set(CMAKE_ASM_NASM_FLAGS "-felf64 -w-number-overflow -I${CMAKE_CURRENT_SOURCE_DIR}/assembly/") 11 | set(CMAKE_ASM_NASM_COMPILE_OBJECT " ${CMAKE_ASM_NASM_FLAGS} -o ") 12 | set(CMAKE_EXE_LINKER_FLAGS "--gc-sections -T${LINKER_SCRIPT}") 13 | set(CMAKE_ASM_NASM_LINK_EXECUTABLE "ld.lld -o ") 14 | 15 | set(XARGO_TOOL $ENV{HOME}/.cargo/bin/xargo-m) 16 | set(RUST_PROJECT ${PROJECT_NAME}.a) 17 | set(RUST_OUTPUT_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/target/${BUILD_TARGET}) 18 | 19 | # force-frame-pointers=yes is needed to allow stack traces to work properly 20 | # See src/debug/stack_trace 21 | set(COMMAND_PREFIX RUST_TARGET_PATH=${CMAKE_CURRENT_SOURCE_DIR} RUSTFLAGS=-Cforce-frame-pointers=yes) 22 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 23 | set(RUST_BUILD ${RUST_OUTPUT_PREFIX}/debug/lib${PROJECT_NAME}.a) 24 | else () 25 | set(RUST_BUILD ${RUST_OUTPUT_PREFIX}/release/lib${PROJECT_NAME}.a) 26 | endif () 27 | 28 | file(GLOB_RECURSE ASSEMBLY_SOURCES assembly/*.asm) 29 | 30 | add_custom_target(${RUST_PROJECT}) 31 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 32 | add_custom_command(TARGET ${RUST_PROJECT} PRE_BUILD 33 | COMMAND ${COMMAND_PREFIX} ${XARGO_TOOL} build --target ${BUILD_TARGET} 34 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 35 | else () 36 | add_custom_command(TARGET ${RUST_PROJECT} PRE_BUILD 37 | COMMAND ${COMMAND_PREFIX} ${XARGO_TOOL} build --release --target ${BUILD_TARGET} 38 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 39 | endif () 40 | 41 | add_executable(${KERNEL_BINARY} ${ASSEMBLY_SOURCES}) 42 | set_target_properties(${KERNEL_BINARY} PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) 43 | add_dependencies(${KERNEL_BINARY} ${RUST_PROJECT}) 44 | target_link_libraries(${KERNEL_BINARY} ${RUST_BUILD}) 45 | -------------------------------------------------------------------------------- /kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel" 3 | version = "0.1.0" 4 | 5 | [lib] 6 | crate-type = ["staticlib"] 7 | 8 | [dependencies] 9 | rlibc = "*" 10 | multiboot2 = "^0.5.0" 11 | x86_64 = "^0.1.2" 12 | spin = "^0.4.8" 13 | rustc-demangle = "^0.1.8" 14 | bitflags = "^1.0" 15 | bit_field = "^0.7.0" 16 | linked_list_allocator = "^0.6.2" 17 | -------------------------------------------------------------------------------- /kernel/Xargo.toml: -------------------------------------------------------------------------------- 1 | [dependencies] 2 | alloc = {} 3 | -------------------------------------------------------------------------------- /kernel/assembly/boot_checks.asm: -------------------------------------------------------------------------------- 1 | global check_multiboot 2 | global check_cpuid 3 | global check_long_mode 4 | 5 | bits 32 6 | section .boot_entry 7 | error: 8 | ; prints 'ERR: ' and an error code (one ASCII character in al) to the screen 9 | mov dword [0xb8000], 0x4f524f45 10 | mov dword [0xb8004], 0x4f3a4f52 11 | mov dword [0xb8008], 0x4f204f20 12 | mov byte [0xb800a], al 13 | hlt 14 | 15 | check_multiboot: 16 | ; returns error code 0 if the bootloader is not Multiboot-compliant 17 | cmp eax, 0x36d76289 18 | jne .no_multiboot 19 | ret 20 | .no_multiboot: 21 | mov al, "0" 22 | jmp error 23 | 24 | check_cpuid: 25 | ; returns error code 1 if the CPUID instruction is unsupported 26 | pushfd 27 | pop eax 28 | mov ecx, eax 29 | xor eax, 1 << 21 30 | push eax 31 | popfd 32 | pushfd 33 | pop eax 34 | push ecx 35 | popfd 36 | cmp eax, ecx 37 | je .no_cpuid 38 | ret 39 | .no_cpuid: 40 | mov al, "1" 41 | jmp error 42 | 43 | check_long_mode: 44 | ; test if extended processor info in available 45 | mov eax, 0x80000000 ; implicit argument for CPUID 46 | cpuid ; get highest supported argument 47 | cmp eax, 0x80000001 ; it needs to be at least 0x80000001 48 | jb .no_long_mode ; if it's less, the CPU is too old for long mode 49 | 50 | ; use extended info to test if long mode is available 51 | mov eax, 0x80000001 ; argument for extended processor info 52 | cpuid ; returns various feature bits in ecx and edx 53 | test edx, 1 << 29 ; test if the LM-bit is set in the D-register 54 | jz .no_long_mode ; If it's not set, there is no long mode 55 | ret 56 | .no_long_mode: 57 | mov al, "2" 58 | jmp error 59 | -------------------------------------------------------------------------------- /kernel/assembly/boot_entry.asm: -------------------------------------------------------------------------------- 1 | %include "constants.inc" 2 | 3 | global start 4 | 5 | extern boot_entry 6 | extern check_multiboot 7 | extern check_cpuid 8 | extern check_long_mode 9 | extern setup_page_tables 10 | extern enable_paging 11 | extern invalidate_tables 12 | extern pml4_table 13 | extern boot_stack_top 14 | 15 | ; This section is NOT in the higher half 16 | section .boot_entry 17 | bits 32 18 | start: 19 | mov esp, boot_stack_top - KERNEL_BASE ; set up stack 20 | mov edi, ebx 21 | 22 | ; See boot_checks.asm 23 | call check_multiboot 24 | call check_cpuid 25 | call check_long_mode 26 | 27 | ; See boot_paging.asm 28 | call setup_page_tables 29 | call enable_paging 30 | 31 | lgdt [gdt64.pointer_low - KERNEL_BASE] 32 | 33 | ; update selectors 34 | mov ax, gdt64.data 35 | mov ss, ax 36 | mov ds, ax 37 | mov es, ax 38 | 39 | jmp gdt64.code:prestart64 40 | 41 | bits 64 42 | prestart64: 43 | ; Jump to the higher half 44 | mov rax, start64 45 | jmp rax 46 | 47 | ; This section is in the higher half 48 | ; See linker.ld 49 | section .text 50 | start64: 51 | mov rsp, boot_stack_top 52 | mov rax, gdt64.pointer 53 | lgdt [rax] 54 | 55 | ; update selectors 56 | mov ax, 0 57 | mov ss, ax 58 | mov ds, ax 59 | mov es, ax 60 | mov fs, ax 61 | mov gs, ax 62 | 63 | jmp start64_2 64 | 65 | start64_2: 66 | ; We don't need any pages mapped in the 67 | ; lower half anymore, so we unmap them 68 | ; and invalidate the entry 69 | mov rax, pml4_table 70 | mov qword [rax], 0 71 | invlpg [0] 72 | .create_null_frame: 73 | ; Set the base pointer to null 74 | xor rbp, rbp 75 | .entry: 76 | ; Enter rust code 77 | call boot_entry 78 | 79 | section .rodata 80 | align 8 81 | gdt64: 82 | dq 0 ; zero entry 83 | .code: equ $ - gdt64 84 | ; Reserved, Present, Readable, Executable, Long Mode 85 | dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment 86 | .data: equ $ - gdt64 87 | ; Reserved, Present, Writable 88 | dq (1<<44) | (1<<47) | (1<<41) ; data segment 89 | .end: 90 | .pointer: 91 | dw gdt64.end - gdt64 - 1 92 | dq gdt64 93 | .pointer_low: 94 | dw gdt64.end - gdt64 - 1 95 | dq gdt64 - KERNEL_BASE -------------------------------------------------------------------------------- /kernel/assembly/boot_paging.asm: -------------------------------------------------------------------------------- 1 | %include "constants.inc" 2 | 3 | global setup_page_tables 4 | global enable_paging 5 | global invalidate_tables 6 | global pml4_table 7 | global boot_stack_top 8 | 9 | section .boot_entry 10 | bits 32 11 | setup_page_tables: 12 | ; map first and 510th PML4 entries to PDP table 13 | ; 14 | ; We want the 510th mapped because our higher 15 | ; half kernel's address is located in the 16 | ; 510th P3 page table 17 | mov eax, pdp_table - KERNEL_BASE 18 | or eax, 0b11 ; present + writable 19 | mov [pml4_table - KERNEL_BASE], eax 20 | mov [pml4_table - KERNEL_BASE + 510 * 8], eax 21 | ; map first PDP entry to PD table 22 | mov eax, pd_table - KERNEL_BASE 23 | or eax, 0b11 ; present + writable 24 | mov [pdp_table - KERNEL_BASE], eax 25 | ; map each PD entry to a huge (2MiB) page 26 | mov ecx, 0 27 | .next_pd_entry: 28 | mov eax, 0x200000 29 | mul ecx 30 | or eax, 0b10000011 ; present + writable + huge 31 | mov [pd_table - KERNEL_BASE + ecx * 8], eax 32 | inc ecx 33 | cmp ecx, 512 34 | jne .next_pd_entry 35 | .recursive_pml4_table: 36 | mov eax, pml4_table - KERNEL_BASE 37 | or eax, 0b11 ; present + writable 38 | mov [pml4_table - KERNEL_BASE + 511 * 8], eax 39 | ret 40 | 41 | enable_paging: 42 | ; load PML4 to cr3 register 43 | mov eax, pml4_table - KERNEL_BASE 44 | mov cr3, eax 45 | 46 | ; enable PAE flag in cr4 47 | mov eax, cr4 48 | or eax, 1 << 5 49 | mov cr4, eax 50 | 51 | ; set the long mode bit in the EFER MSR 52 | mov ecx, 0xC0000080 53 | rdmsr 54 | or eax, 1 << 8 55 | wrmsr 56 | 57 | ; enable paging in the cr0 register 58 | mov eax, cr0 59 | or eax, 1 << 31 60 | mov cr0, eax 61 | ret 62 | 63 | section .bss 64 | align 4096 65 | pml4_table: 66 | resb 4096 67 | pdp_table: 68 | resb 4096 69 | pd_table: 70 | resb 4096 71 | page_table: 72 | resb 4096 73 | ; We put the stack above the page tables 74 | ; to later create a guard page that protects 75 | ; us from stack overflows 76 | boot_stack_bottom: 77 | resb 16 * 4096 78 | boot_stack_top: 79 | -------------------------------------------------------------------------------- /kernel/assembly/constants.inc: -------------------------------------------------------------------------------- 1 | %ifndef CONSTANTS 2 | %define CONSTANTS 3 | %define KERNEL_BASE 0xFFFFFF0000000000 4 | %endif -------------------------------------------------------------------------------- /kernel/assembly/multiboot_header.asm: -------------------------------------------------------------------------------- 1 | MULTIBOOT_MAGIC_NUMBER: equ 0xe85250d6 2 | ARCHITECTURE_i386: equ 0 3 | HEADER_LENGTH: equ header_end - header_start 4 | MULTIBOOT_CHECKSUM: equ 0x100000000 - (MULTIBOOT_MAGIC_NUMBER + HEADER_LENGTH) 5 | 6 | struc MultibootTagHeader 7 | .type: resw 1 8 | .flags: resw 1 9 | .size: resd 1 10 | endstruc 11 | 12 | section .multiboot_header 13 | header_start: 14 | dd MULTIBOOT_MAGIC_NUMBER 15 | dd ARCHITECTURE_i386 16 | dd HEADER_LENGTH 17 | 18 | dd MULTIBOOT_CHECKSUM 19 | 20 | end_tag: istruc MultibootTagHeader 21 | at MultibootTagHeader.type, dw 0 22 | at MultibootTagHeader.flags, dw 0 23 | at MultibootTagHeader.size, dd 8 24 | iend 25 | 26 | header_end: 27 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | 3 | KERNEL_BASE = 0xFFFFFF0000000000; 4 | 5 | # ALIGN(4K) is used everywhere to align all the kernel sections 6 | # on a page boundary. This is so we can remap the kernel and set 7 | # the appropriate flags for each section. 8 | # See paging/functions::remap_kernel_sections 9 | SECTIONS { 10 | # Set the current address to the lower half 11 | . = 1M; 12 | 13 | .boot_header : 14 | { 15 | KEEP(*(.multiboot_header)) 16 | . = ALIGN(4K); 17 | } 18 | 19 | .boot_entry : { 20 | *(.boot_entry) 21 | . = ALIGN(4K); 22 | } 23 | 24 | # All sections after this have addresses 25 | # in the higher half 26 | . += KERNEL_BASE; 27 | 28 | .rodata : AT(ADDR(.rodata) - KERNEL_BASE) { 29 | *(.rodata .rodata.*) 30 | *(.eh_frame) 31 | . = ALIGN(4K); 32 | } 33 | 34 | .text : AT(ADDR(.text) - KERNEL_BASE) { 35 | *(.text .text.*) 36 | . = ALIGN(4K); 37 | } 38 | 39 | .data : AT(ADDR(.data) - KERNEL_BASE) { 40 | *(.data .data.*) 41 | . = ALIGN(4K); 42 | } 43 | 44 | .bss : AT(ADDR(.bss) - KERNEL_BASE) { 45 | *(.bss .bss.*) 46 | . = ALIGN(4K); 47 | } 48 | 49 | .got : AT(ADDR(.got) - KERNEL_BASE) { 50 | *(.got) 51 | . = ALIGN(4K); 52 | } 53 | 54 | .got.plt : AT(ADDR(.got.plt) - KERNEL_BASE) { 55 | *(.got.plt) 56 | . = ALIGN(4K); 57 | } 58 | 59 | .gcc_except_table : ALIGN(4K) { 60 | *(.gcc_except_table) 61 | . = ALIGN(4K); 62 | } 63 | } -------------------------------------------------------------------------------- /kernel/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /kernel/src/debug/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::stack_trace::stack_trace; 2 | 3 | pub mod stack_trace; 4 | pub mod symbols; 5 | -------------------------------------------------------------------------------- /kernel/src/debug/stack_trace.rs: -------------------------------------------------------------------------------- 1 | use alloc::BTreeMap; 2 | use paging::VirtualAddress; 3 | use rustc_demangle::Demangle; 4 | 5 | pub extern fn stack_trace() { 6 | // When force-frame-pointers is enabled, every function 7 | // has these instructions at the start: 8 | // 9 | // push rbp 10 | // mov rbp, rsp 11 | // 12 | // When the call instruction is used, the return address 13 | // (which contains the next instruction to execute when 14 | // the called function has returned) is pushed onto the stack. 15 | // Thus, the return address is directly above (because 16 | // the stack grows downwards) the pushed base pointer. 17 | // We can use this to create a stack trace. 18 | 19 | println!("Stack trace:"); 20 | let mut base_pointer: *const usize; 21 | 22 | // Get the address of pushed base pointer 23 | unsafe { asm!("mov rax, rbp" : "={rax}"(base_pointer) ::: "intel") } 24 | 25 | let symbols = super::symbols::SYMBOL_TABLE.try(); 26 | 27 | // Before entering boot_entry we set the base pointer to null (0) 28 | // This way, we can determine when to stop walking the stack 29 | // See the start64_2 function in boot_entry.asm 30 | while !base_pointer.is_null() { 31 | 32 | // The return address is above the pushed base pointer 33 | let return_address = unsafe { *(base_pointer.offset(1)) } as usize; 34 | let return_address = VirtualAddress::new(return_address); 35 | 36 | // If we haven't loaded the symbol table yet just 37 | // print the raw return address 38 | if let Some(symbols) = symbols { 39 | show_function_call(return_address, symbols); 40 | } else { 41 | println!(" Call site: {:#?}", return_address); 42 | } 43 | 44 | // The pushed base pointer is the address to the previous stack frame 45 | base_pointer = unsafe { (*base_pointer) as *const usize }; 46 | } 47 | } 48 | 49 | fn show_function_call(address: VirtualAddress, symbols: &BTreeMap) { 50 | // The address of every instruction in a function is 51 | // after the address of the function itself. Thus, 52 | // we find the symbol with the greatest address that's 53 | // lower than the return address 54 | 55 | let mut range = symbols.range(..address.clone()); 56 | if let Some((_, identifier)) = range.next_back() { 57 | println!(" Call site: {:#?}", identifier); 58 | } else { 59 | println!(" Call site: {:#?}", address); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /kernel/src/debug/symbols.rs: -------------------------------------------------------------------------------- 1 | use alloc::BTreeMap; 2 | use alloc::String; 3 | use alloc::Vec; 4 | use graph::Provider; 5 | use paging::VirtualAddress; 6 | use rustc_demangle::Demangle; 7 | use spin::Once; 8 | 9 | // Demangle stores a reference to a string so the 10 | // symbol table string has to have a static lifetime 11 | static TABLE_STRING: Once = Once::new(); 12 | pub static SYMBOL_TABLE: Once> = Once::new(); 13 | 14 | pub fn load_kernel_symbols() { 15 | if let Some(string) = load_symbol_table() { 16 | let string = TABLE_STRING.call_once(|| string); 17 | SYMBOL_TABLE.call_once(|| parse_symbols(string)); 18 | } 19 | } 20 | 21 | pub fn load_symbol_table() -> Option { 22 | macro_rules! table_location { () => { "kernel/symbols.table" }; } 23 | let mut status = ::display::text_mode::BootStatus::new("Loading kernel debug symbols"); 24 | 25 | let symbol_table_path = ::graph::Location::parse(concat!("boot_disk/", table_location!())); 26 | 27 | // Load the symbol table file from our boot disk 28 | let symbol_table = ::graph::ROOT_PROVIDER.lock().open(&symbol_table_path.as_slice()); 29 | let symbol_table = if let Some(mut table) = symbol_table { 30 | table.read_all() 31 | } else { 32 | status.set_warning().with_message(); 33 | println!("Missing {} file in boot disk", table_location!()); 34 | return None; 35 | }; 36 | 37 | // Copy the file data as a string for easier parsing 38 | let symbol_table = String::from_utf8(symbol_table); 39 | let table_string = if let Ok(symbol_table) = symbol_table { 40 | symbol_table 41 | } else { 42 | status.set_failure().with_message(); 43 | eprintln!("Failed to parse symbol table string"); 44 | return None; 45 | }; 46 | Some(table_string) 47 | } 48 | 49 | pub fn parse_symbols(table_string: &str) -> BTreeMap { 50 | let mut status = ::display::text_mode::BootStatus::new("Demangling kernel symbol identifiers"); 51 | let mut map = BTreeMap::new(); 52 | let mut skipped_count = 0; 53 | let mut parsed_count = 0; 54 | 55 | let entries = table_string.split('\n'); 56 | for entry in entries { 57 | let segments: Vec<&str> = entry.split_whitespace().collect(); 58 | 59 | // Each line in the symbol table must has three columns 60 | // because we use the first and third columns 61 | if segments.len() < 3 { 62 | skipped_count += 1; 63 | continue; 64 | } 65 | 66 | // The first column contains the address of the symbol, 67 | // but it is in a hexadecimal string form so we have to 68 | // convert it to a number 69 | let address = usize::from_str_radix(segments[0], 16); 70 | let address = if let Ok(address) = address { 71 | VirtualAddress::new(address) 72 | } else { 73 | skipped_count += 1; 74 | continue; 75 | }; 76 | 77 | // The third column contains the name of the symbol, 78 | // but it has been mangled by the Rust compiler. We 79 | // demangle it here. 80 | let identifier = ::rustc_demangle::demangle(segments[2]); 81 | map.insert(address, identifier); 82 | parsed_count += 1; 83 | } 84 | 85 | status.set_success().with_message(); 86 | println!("Successfully parsed {} out of {} entries", parsed_count, parsed_count + skipped_count); 87 | map 88 | } -------------------------------------------------------------------------------- /kernel/src/display/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod text_mode; -------------------------------------------------------------------------------- /kernel/src/display/text_mode/buffer.rs: -------------------------------------------------------------------------------- 1 | use core::ops; 2 | use super::*; 3 | 4 | #[derive(Debug, Copy, Clone)] 5 | pub struct TextCell { 6 | pub character: Character, 7 | pub foreground: LowDepthColour, 8 | pub background: LowDepthColour, 9 | } 10 | 11 | impl Default for TextCell { 12 | fn default() -> Self { 13 | TextCell { 14 | character: b' ', 15 | foreground: LowDepthColour::FOREGROUND, 16 | background: LowDepthColour::BACKGROUND, 17 | } 18 | } 19 | } 20 | 21 | pub trait Buffer: TextDisplay + ops::Index + ops::IndexMut { 22 | fn flush_cursor(&mut self); 23 | fn flush_cell(&mut self, position: &Position); 24 | 25 | fn flush_cells(&mut self) { 26 | for row in 0..self.height() { 27 | self.flush_row(row); 28 | } 29 | } 30 | 31 | fn flush_all(&mut self) { 32 | self.flush_cells(); 33 | self.flush_cursor(); 34 | } 35 | 36 | fn flush_row(&mut self, row: usize) { 37 | for column in 0..self.width() { 38 | self.flush_cell(&Position { column, row }); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /kernel/src/display/text_mode/buffers/boot_buffer.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | 3 | const WIDTH: Width = 80; 4 | const HEIGHT: Height = 25; 5 | 6 | pub struct BootBuffer { 7 | display: D, 8 | buffer: [[TextCell; HEIGHT]; WIDTH], 9 | cursor: Position, 10 | } 11 | 12 | impl BootBuffer { 13 | pub fn new(display: D) -> BootBuffer { 14 | BootBuffer { 15 | display, 16 | buffer: [[TextCell::default(); 25]; 80], 17 | cursor: Position::default(), 18 | } 19 | } 20 | } 21 | 22 | impl Buffer for BootBuffer { 23 | fn flush_cursor(&mut self) { 24 | self.display.set_cursor(&self.cursor); 25 | } 26 | 27 | fn flush_cell(&mut self, position: &Position) { 28 | let text_cell = &self.buffer[position.column][position.row]; 29 | self.display.set_cell(position, text_cell.character); 30 | self.display.set_background(position, &text_cell.background); 31 | self.display.set_foreground(position, &text_cell.foreground); 32 | } 33 | } 34 | 35 | impl TextDisplay for BootBuffer { 36 | fn width(&self) -> Width { 37 | WIDTH 38 | } 39 | 40 | fn height(&self) -> Height { 41 | HEIGHT 42 | } 43 | 44 | fn set_cell(&mut self, position: &Position, character: Character) { 45 | self.buffer[position.column][position.row].character = character; 46 | } 47 | 48 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour) { 49 | self.buffer[position.column][position.row].background = *colour; 50 | } 51 | 52 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour) { 53 | self.buffer[position.column][position.row].foreground = *colour; 54 | } 55 | 56 | fn set_cursor(&mut self, position: &Position) { 57 | self.cursor = position.clone(); 58 | } 59 | } 60 | 61 | impl ::core::ops::Index for BootBuffer { 62 | type Output = [TextCell]; 63 | 64 | fn index(&self, index: usize) -> &[TextCell] { 65 | &self.buffer[index] 66 | } 67 | } 68 | 69 | impl ::core::ops::IndexMut for BootBuffer { 70 | fn index_mut(&mut self, index: usize) -> &mut [TextCell] { 71 | &mut self.buffer[index] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/buffers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::boot_buffer::BootBuffer; 2 | pub use self::window_buffer::WindowBuffer; 3 | 4 | pub mod boot_buffer; 5 | pub mod window_buffer; 6 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/buffers/window_buffer.rs: -------------------------------------------------------------------------------- 1 | use alloc::Vec; 2 | use display::text_mode::*; 3 | 4 | pub struct WindowBuffer { 5 | display: D, 6 | 7 | position: Position, 8 | width: Width, 9 | height: Height, 10 | 11 | buffer: Vec, 12 | cursor: Position, 13 | } 14 | 15 | impl WindowBuffer { 16 | pub fn new(display: D, position: Position, width: Width, height: Height) -> WindowBuffer { 17 | WindowBuffer { 18 | display, 19 | position, 20 | width, 21 | height, 22 | buffer: vec![TextCell::default(); width * height], 23 | cursor: Position::default(), 24 | } 25 | } 26 | } 27 | 28 | impl Buffer for WindowBuffer { 29 | fn flush_cursor(&mut self) { 30 | self.display.set_cursor(&(&self.cursor + &self.position)) 31 | } 32 | 33 | fn flush_cell(&mut self, position: &Position) { 34 | let text_cell = &self.buffer[self.height * position.column + position.row]; 35 | let position = &(position + &self.position); 36 | self.display.set_cell(position, text_cell.character); 37 | self.display.set_background(position, &text_cell.background); 38 | self.display.set_foreground(position, &text_cell.foreground); 39 | } 40 | } 41 | 42 | impl TextDisplay for WindowBuffer { 43 | fn width(&self) -> Width { 44 | self.width 45 | } 46 | 47 | fn height(&self) -> Height { 48 | self.height 49 | } 50 | 51 | fn set_cell(&mut self, position: &Position, character: Character) { 52 | self[position.column][position.row].character = character; 53 | } 54 | 55 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour) { 56 | self[position.column][position.row].background = *colour; 57 | } 58 | 59 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour) { 60 | self[position.column][position.row].foreground = *colour; 61 | } 62 | 63 | fn set_cursor(&mut self, position: &Position) { 64 | self.cursor = position.clone(); 65 | } 66 | } 67 | 68 | impl ::core::ops::Index for WindowBuffer { 69 | type Output = [TextCell]; 70 | 71 | fn index(&self, index: usize) -> &[TextCell] { 72 | let column = self.height * index; 73 | &self.buffer[column..(column + self.height)] 74 | } 75 | } 76 | 77 | impl ::core::ops::IndexMut for WindowBuffer { 78 | fn index_mut(&mut self, index: usize) -> &mut [TextCell] { 79 | let column = self.height * index; 80 | &mut self.buffer[column..(column + self.height)] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/drivers/global_facade.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | use display::text_mode::functions::SYSTEM_DISPLAY; 3 | 4 | // This structure allows multiple printers to use 5 | // the same display driver 6 | pub struct GlobalFacade; 7 | 8 | impl TextDisplay for GlobalFacade { 9 | fn width(&self) -> usize { 10 | SYSTEM_DISPLAY.lock().width() 11 | } 12 | 13 | fn height(&self) -> usize { 14 | SYSTEM_DISPLAY.lock().height() 15 | } 16 | 17 | fn set_cell(&mut self, position: &Position, character: u8) { 18 | SYSTEM_DISPLAY.lock().set_cell(position, character); 19 | } 20 | 21 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour) { 22 | SYSTEM_DISPLAY.lock().set_background(position, colour); 23 | } 24 | 25 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour) { 26 | SYSTEM_DISPLAY.lock().set_foreground(position, colour); 27 | } 28 | 29 | fn set_cursor(&mut self, position: &Position) { 30 | SYSTEM_DISPLAY.lock().set_cursor(position); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::global_facade::GlobalFacade; 2 | pub use self::vga_text_driver::VgaTextDriver; 3 | 4 | pub mod vga_text_driver; 5 | pub mod global_facade; 6 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/drivers/vga_text_driver.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | 3 | type Buffer = [[VgaCharacter; VgaTextDriver::WIDTH]; VgaTextDriver::HEIGHT]; 4 | 5 | pub struct VgaTextDriver { 6 | buffer: &'static mut Buffer, 7 | } 8 | 9 | impl VgaTextDriver { 10 | const WIDTH: Width = 80; 11 | const HEIGHT: Height = 25; 12 | 13 | const COMMAND_PORT: u16 = 0x3d4; 14 | const DATA_PORT: u16 = 0x3d5; 15 | 16 | /// Creates an interface over the VGA frame buffer 17 | /// 18 | /// # Safety 19 | /// 20 | /// Only one VgaTextDriver instance should exist 21 | /// The VGA buffer should be paged at `0xb8000 + KERNEL_BASE` 22 | /// 23 | pub unsafe fn new() -> VgaTextDriver { 24 | VgaTextDriver::enable_cursor(); 25 | VgaTextDriver { 26 | // The buffer page is located in the higher half 27 | buffer: &mut *((0xb8000 + ::KERNEL_BASE) as *mut Buffer), 28 | } 29 | } 30 | 31 | fn match_colour_code(colour: &LowDepthColour) -> u8 { 32 | match *colour { 33 | LowDepthColour::Black => 0, 34 | LowDepthColour::Blue => 1, 35 | LowDepthColour::Green => 2, 36 | LowDepthColour::Cyan => 3, 37 | LowDepthColour::Red => 4, 38 | LowDepthColour::Magenta => 5, 39 | LowDepthColour::Brown => 6, 40 | LowDepthColour::LightGray => 7, 41 | LowDepthColour::DarkGray => 8, 42 | LowDepthColour::LightBlue => 9, 43 | LowDepthColour::LightGreen => 10, 44 | LowDepthColour::LightCyan => 11, 45 | LowDepthColour::LightRed => 12, 46 | LowDepthColour::Pink => 13, 47 | LowDepthColour::Yellow => 14, 48 | LowDepthColour::White => 15, 49 | } 50 | } 51 | 52 | fn enable_cursor() { 53 | use x86_64::instructions::port::outb; 54 | use x86_64::instructions::port::inb; 55 | unsafe { 56 | outb(VgaTextDriver::COMMAND_PORT, 0x0A); 57 | outb(VgaTextDriver::DATA_PORT, inb(VgaTextDriver::DATA_PORT) & 0xC0); 58 | outb(VgaTextDriver::COMMAND_PORT, 0x0B); 59 | outb(VgaTextDriver::DATA_PORT, (inb(0x3E0) & 0xE0) | (VgaTextDriver::HEIGHT as u8 - 1)); 60 | } 61 | } 62 | } 63 | 64 | impl TextDisplay for VgaTextDriver { 65 | fn width(&self) -> Width { 66 | VgaTextDriver::WIDTH 67 | } 68 | 69 | fn height(&self) -> Height { 70 | VgaTextDriver::HEIGHT 71 | } 72 | 73 | fn set_cell(&mut self, position: &Position, character: Character) { 74 | self.buffer[position.row][position.column].character = character; 75 | } 76 | 77 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour) { 78 | let buffer_colour = &mut self.buffer[position.row][position.column].colour; 79 | *buffer_colour &= 0b1111_0000; 80 | *buffer_colour |= VgaTextDriver::match_colour_code(colour) << 4; 81 | } 82 | 83 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour) { 84 | let buffer_colour = &mut self.buffer[position.row][position.column].colour; 85 | *buffer_colour &= 0b0000_1111; 86 | *buffer_colour |= VgaTextDriver::match_colour_code(colour); 87 | } 88 | 89 | fn set_cursor(&mut self, position: &Position) { 90 | use x86_64::instructions::port::outb; 91 | use x86_64::instructions::port::outw; 92 | let position = (position.row * VgaTextDriver::WIDTH) + position.column; 93 | unsafe { 94 | outb(VgaTextDriver::COMMAND_PORT, 0x0f); 95 | outw(VgaTextDriver::DATA_PORT, position as u16 & 0xff); 96 | outb(VgaTextDriver::COMMAND_PORT, 0x0e); 97 | outw(VgaTextDriver::DATA_PORT, (position as u16 >> 8) & 0xff); 98 | } 99 | } 100 | } 101 | 102 | #[repr(C)] 103 | struct VgaCharacter { 104 | character: u8, 105 | colour: u8, 106 | } 107 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/functions.rs: -------------------------------------------------------------------------------- 1 | use utility::Global; 2 | use super::buffers::BootBuffer; 3 | use super::drivers::GlobalFacade; 4 | use super::printers::ScrollPrinter; 5 | use super::drivers::vga_text_driver::VgaTextDriver; 6 | 7 | pub static SYSTEM_DISPLAY: Global = Global::new("SYSTEM_DISPLAY"); 8 | pub static SYSTEM_PRINTER: Global>> = Global::new("SYSTEM_PRINTER"); 9 | 10 | pub fn initialize() { 11 | SYSTEM_DISPLAY.set(unsafe { VgaTextDriver::new() }); 12 | SYSTEM_PRINTER.set(ScrollPrinter::new(BootBuffer::new(GlobalFacade {}))); 13 | } 14 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/low_depth_colour.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | #[derive(Debug, Copy, Clone, PartialEq)] 3 | pub enum LowDepthColour { 4 | Black, 5 | Blue, 6 | Green, 7 | Cyan, 8 | Red, 9 | Magenta, 10 | Brown, 11 | LightGray, 12 | DarkGray, 13 | LightBlue, 14 | LightGreen, 15 | LightCyan, 16 | LightRed, 17 | Pink, 18 | Yellow, 19 | White, 20 | } 21 | 22 | impl LowDepthColour { 23 | pub const BACKGROUND: LowDepthColour = LowDepthColour::Black; 24 | pub const FOREGROUND: LowDepthColour = LowDepthColour::White; 25 | } -------------------------------------------------------------------------------- /kernel/src/display/text_mode/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::buffer::Buffer; 2 | pub use self::buffer::TextCell; 3 | pub use self::functions::SYSTEM_DISPLAY; 4 | pub use self::functions::SYSTEM_PRINTER; 5 | pub use self::low_depth_colour::LowDepthColour; 6 | pub use self::position::Position; 7 | pub use self::printer::Printer; 8 | pub use self::text_display::TextDisplay; 9 | pub use self::utility::BootStatus; 10 | 11 | #[macro_use] 12 | pub mod writers; 13 | pub mod low_depth_colour; 14 | pub mod text_display; 15 | pub mod drivers; 16 | pub mod buffers; 17 | pub mod printer; 18 | pub mod printers; 19 | pub mod buffer; 20 | pub mod position; 21 | pub mod utility; 22 | pub mod functions; 23 | 24 | pub type Width = usize; 25 | pub type Height = usize; 26 | pub type Character = u8; 27 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/position.rs: -------------------------------------------------------------------------------- 1 | use super::Height; 2 | use super::Width; 3 | 4 | #[derive(Debug, Clone, PartialEq)] 5 | pub struct Position { 6 | pub column: Width, 7 | pub row: Height, 8 | } 9 | 10 | impl Default for Position { 11 | fn default() -> Self { 12 | Self { column: 0, row: 0 } 13 | } 14 | } 15 | 16 | impl<'a, 'b> ::core::ops::Add<&'b Position> for &'a Position { 17 | type Output = Position; 18 | 19 | fn add(self, rhs: &'b Position) -> >::Output { 20 | Position { 21 | column: self.column + rhs.column, 22 | row: self.row + rhs.row, 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/printer.rs: -------------------------------------------------------------------------------- 1 | use super::LowDepthColour; 2 | 3 | pub trait Printer { 4 | fn print(&mut self, string: &str) { 5 | self.print_coloured(string, &LowDepthColour::BACKGROUND, &LowDepthColour::FOREGROUND); 6 | } 7 | 8 | fn print_error(&mut self, string: &str) { 9 | self.print_coloured(string, &LowDepthColour::BACKGROUND, &LowDepthColour::LightRed); 10 | } 11 | 12 | fn print_coloured(&mut self, string: &str, background: &LowDepthColour, foreground: &LowDepthColour); 13 | } 14 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/printers/list_printer.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | 3 | pub struct ListPrinter { 4 | buffer: B, 5 | } 6 | 7 | impl ListPrinter { 8 | pub fn new(buffer: B) -> ListPrinter { 9 | ListPrinter { 10 | buffer, 11 | } 12 | } 13 | 14 | pub fn flush_all(&mut self) { 15 | self.buffer.flush_cells(); 16 | } 17 | 18 | pub fn set_list>(&mut self, list: &[T]) { 19 | for row in 0..self.buffer.height() { 20 | let item = list.get(row).and_then(|item| Some(item.as_ref().as_bytes())); 21 | for column in 0..self.buffer.width() { 22 | let character = item.and_then(|item| item.get(column)).unwrap_or(&b' '); 23 | self.buffer[column][row].character = *character; 24 | } 25 | } 26 | self.buffer.flush_cells(); 27 | } 28 | 29 | pub fn set_foreground_colour(&mut self, row: usize, colour: &LowDepthColour) { 30 | for column in 0..self.buffer.width() { 31 | self.buffer[column][row].foreground = *colour; 32 | } 33 | self.buffer.flush_row(row); 34 | } 35 | } -------------------------------------------------------------------------------- /kernel/src/display/text_mode/printers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::list_printer::ListPrinter; 2 | pub use self::scroll_printer::ScrollPrinter; 3 | pub use self::selectable_list::SelectableList; 4 | 5 | pub mod scroll_printer; 6 | pub mod list_printer; 7 | pub mod selectable_list; 8 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/printers/scroll_printer.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | 3 | pub struct ScrollPrinter { 4 | buffer: B, 5 | column: usize, 6 | row: usize, 7 | } 8 | 9 | impl ScrollPrinter { 10 | pub fn new(buffer: B) -> ScrollPrinter { 11 | let final_row = buffer.height() - 1; 12 | ScrollPrinter { 13 | buffer, 14 | column: 0, 15 | row: final_row, 16 | } 17 | } 18 | 19 | pub fn flush_all(&mut self) { 20 | self.buffer.flush_all(); 21 | } 22 | 23 | fn update_cursor(&mut self) { 24 | self.buffer.set_cursor(&Position { 25 | column: self.column, 26 | row: self.row, 27 | }); 28 | } 29 | 30 | fn new_line(&mut self) { 31 | for row in 1..self.buffer.height() { 32 | for column in 0..self.buffer.width() { 33 | self.buffer[column][row - 1] = self.buffer[column][row]; 34 | } 35 | } 36 | let final_row = self.final_row(); 37 | self.clear_row(final_row); 38 | self.column = 0; 39 | } 40 | 41 | fn backspace(&mut self) { 42 | if self.column > 0 { 43 | self.column -= 1; 44 | } 45 | } 46 | 47 | fn clear_row(&mut self, row: usize) { 48 | for column in 0..self.buffer.width() { 49 | self.buffer[column][row] = TextCell::default(); 50 | } 51 | } 52 | 53 | fn final_row(&self) -> usize { 54 | self.buffer.height() - 1 55 | } 56 | } 57 | 58 | impl Printer for ScrollPrinter { 59 | fn print_coloured(&mut self, string: &str, background: &LowDepthColour, foreground: &LowDepthColour) { 60 | let mut new_line_printed = false; 61 | for character in string.bytes() { 62 | match character { 63 | b'\n' => { 64 | self.new_line(); 65 | new_line_printed = true; 66 | } 67 | b'\x08' => { 68 | self.backspace(); 69 | } 70 | _ => { 71 | if self.column == self.buffer.width() { 72 | self.new_line(); 73 | new_line_printed = true; 74 | } 75 | 76 | let cell = &mut self.buffer[self.column][self.row]; 77 | cell.character = character; 78 | cell.background = *background; 79 | cell.foreground = *foreground; 80 | 81 | self.column += 1; 82 | } 83 | } 84 | } 85 | 86 | self.update_cursor(); 87 | if new_line_printed { 88 | self.buffer.flush_all(); 89 | } else { 90 | self.buffer.flush_row(self.row); 91 | self.buffer.flush_cursor(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/printers/selectable_list.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | use super::ListPrinter; 3 | 4 | pub struct SelectableList { 5 | list_printer: ListPrinter, 6 | selected: usize, 7 | buffer_height: usize, 8 | list_size: usize, 9 | } 10 | 11 | impl SelectableList { 12 | pub fn new(buffer: B) -> SelectableList { 13 | let buffer_height = buffer.height(); 14 | SelectableList { 15 | list_printer: ListPrinter::new(buffer), 16 | selected: 0, 17 | buffer_height, 18 | list_size: 0, 19 | } 20 | } 21 | 22 | pub fn flush_all(&mut self) { 23 | self.list_printer.flush_all(); 24 | } 25 | 26 | pub fn set_list>(&mut self, list: &[T]) { 27 | self.list_printer.set_list(list); 28 | self.list_size = list.len().min(self.buffer_height); 29 | } 30 | 31 | pub fn set_selected(&mut self, index: usize) { 32 | self.list_printer.set_foreground_colour(self.selected, &LowDepthColour::FOREGROUND); 33 | self.list_printer.set_foreground_colour(index, &LowDepthColour::LightGreen); 34 | self.selected = index; 35 | } 36 | 37 | pub fn offset_selected(&mut self, offset: i32) { 38 | if self.list_size == 0 { return; } 39 | 40 | let selected = self.selected as i32; 41 | let mut new_selected = (selected + offset) % self.list_size as i32; 42 | if new_selected < 0 { 43 | new_selected = self.list_size as i32 + new_selected; 44 | } 45 | self.set_selected(new_selected as usize); 46 | } 47 | 48 | pub fn get_selected(&self) -> usize { 49 | self.selected 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/text_display.rs: -------------------------------------------------------------------------------- 1 | use super::LowDepthColour; 2 | use super::Position; 3 | 4 | pub trait TextDisplay { 5 | fn width(&self) -> super::Width; 6 | fn height(&self) -> super::Height; 7 | 8 | fn set_cell(&mut self, position: &Position, character: super::Character); 9 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour); 10 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour); 11 | 12 | fn set_cursor(&mut self, position: &Position); 13 | } -------------------------------------------------------------------------------- /kernel/src/display/text_mode/utility/boot_status.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::LowDepthColour; 2 | use display::text_mode::Printer; 3 | use display::text_mode::SYSTEM_PRINTER; 4 | 5 | pub struct BootStatus { 6 | updated: bool, 7 | } 8 | 9 | impl BootStatus { 10 | const STATUS_LENGTH: usize = 4; 11 | const FULL_STATUS_LENGTH: usize = Self::STATUS_LENGTH + 3; 12 | 13 | pub fn new(message: &'static str) -> BootStatus { 14 | let length = message.len() + Self::FULL_STATUS_LENGTH; 15 | print!("[ -- ] {}", message); 16 | for _ in 0..(length - 1) { print!("\x08"); } 17 | BootStatus { 18 | updated: false, 19 | } 20 | } 21 | 22 | fn reset_cursor(&mut self) -> &mut BootStatus { 23 | for _ in 0..Self::STATUS_LENGTH { print!("\x08"); } 24 | self.updated = true; 25 | self 26 | } 27 | 28 | pub fn with_message(&mut self) { 29 | SYSTEM_PRINTER.lock().print("\n "); 30 | } 31 | 32 | pub fn set_failure(&mut self) -> &mut BootStatus { 33 | SYSTEM_PRINTER.lock().print_coloured("Fail", &LowDepthColour::BACKGROUND, &LowDepthColour::LightRed); 34 | self.reset_cursor() 35 | } 36 | 37 | pub fn set_warning(&mut self) -> &mut BootStatus { 38 | SYSTEM_PRINTER.lock().print_coloured("Warn", &LowDepthColour::BACKGROUND, &LowDepthColour::Yellow); 39 | self.reset_cursor() 40 | } 41 | 42 | pub fn set_success(&mut self) -> &mut Self { 43 | SYSTEM_PRINTER.lock().print_coloured(" Ok ", &LowDepthColour::BACKGROUND, &LowDepthColour::LightGreen); 44 | self.reset_cursor() 45 | } 46 | } 47 | 48 | impl Drop for BootStatus { 49 | fn drop(&mut self) { 50 | if !self.updated { 51 | self.set_success(); 52 | println!(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /kernel/src/display/text_mode/utility/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::boot_status::BootStatus; 2 | 3 | pub mod boot_status; -------------------------------------------------------------------------------- /kernel/src/display/text_mode/writers/debug_writer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | 3 | pub static mut DEBUG_WRITER: DebugWriter = DebugWriter {}; 4 | 5 | macro_rules! debug_print { 6 | ($($arg:tt)*) => ({ 7 | $crate::display::text_mode::writers::DebugWriter::print(format_args!($($arg)*)); 8 | }); 9 | } 10 | 11 | macro_rules! debug_println { 12 | () => (debug_print!("\n")); 13 | ($fmt:expr) => (debug_print!(concat!($fmt, "\n"))); 14 | ($fmt:expr, $($arg:tt)*) => (debug_print!(concat!($fmt, "\n"), $($arg)*)); 15 | } 16 | 17 | pub struct DebugWriter {} 18 | 19 | impl DebugWriter { 20 | pub fn print(arguments: ::core::fmt::Arguments) { 21 | unsafe { DEBUG_WRITER.write_fmt(arguments).unwrap() }; 22 | } 23 | } 24 | 25 | impl ::core::fmt::Write for DebugWriter { 26 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 27 | const COMMUNICATION_PORT: u16 = 0x3f8; 28 | for byte in s.bytes() { 29 | unsafe { 30 | ::x86_64::instructions::port::outb(COMMUNICATION_PORT, byte); 31 | } 32 | } 33 | Ok(()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/writers/default_writer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | 3 | pub static mut DEFAULT_WRITER: DefaultWriter = DefaultWriter {}; 4 | 5 | macro_rules! print { 6 | ($($arg:tt)*) => ({ 7 | $crate::display::text_mode::writers::DefaultWriter::print(format_args!($($arg)*)); 8 | }); 9 | } 10 | 11 | macro_rules! println { 12 | () => (print!("\n")); 13 | ($fmt:expr) => (print!(concat!($fmt, "\n"))); 14 | ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); 15 | } 16 | 17 | pub struct DefaultWriter {} 18 | 19 | impl DefaultWriter { 20 | pub fn print(arguments: ::core::fmt::Arguments) { 21 | unsafe { DEFAULT_WRITER.write_fmt(arguments).unwrap() }; 22 | } 23 | } 24 | 25 | impl ::core::fmt::Write for DefaultWriter { 26 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 27 | use display::text_mode::Printer; 28 | use display::text_mode::SYSTEM_PRINTER; 29 | SYSTEM_PRINTER.lock().print(s); 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/writers/error_writer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | 3 | pub static mut ERROR_WRITER: ErrorWriter = ErrorWriter {}; 4 | 5 | macro_rules! eprint { 6 | ($($arg:tt)*) => ({ 7 | $crate::display::text_mode::writers::ErrorWriter::print(format_args!($($arg)*)); 8 | }); 9 | } 10 | 11 | macro_rules! eprintln { 12 | () => (eprint!("\n")); 13 | ($fmt:expr) => (eprint!(concat!($fmt, "\n"))); 14 | ($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*)); 15 | } 16 | 17 | pub struct ErrorWriter {} 18 | 19 | impl ErrorWriter { 20 | pub fn print(arguments: ::core::fmt::Arguments) { 21 | unsafe { ERROR_WRITER.write_fmt(arguments).unwrap() }; 22 | } 23 | } 24 | 25 | impl ::core::fmt::Write for ErrorWriter { 26 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 27 | use display::text_mode::Printer; 28 | use display::text_mode::SYSTEM_PRINTER; 29 | SYSTEM_PRINTER.lock().print_error(s); 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kernel/src/display/text_mode/writers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::debug_writer::DebugWriter; 2 | pub use self::default_writer::DefaultWriter; 3 | pub use self::error_writer::ErrorWriter; 4 | 5 | #[macro_use] 6 | pub mod default_writer; 7 | #[macro_use] 8 | pub mod error_writer; 9 | #[macro_use] 10 | #[allow(unused_macros)] 11 | #[allow(dead_code)] 12 | pub mod debug_writer; 13 | -------------------------------------------------------------------------------- /kernel/src/graph/functions.rs: -------------------------------------------------------------------------------- 1 | use alloc::Vec; 2 | use super::providers::Root; 3 | use utility::Global; 4 | 5 | pub static ROOT_PROVIDER: Global = Global::new("ROOT_PROVIDER"); 6 | 7 | pub fn load_boot_disk(boot_information: &::multiboot2::BootInformation) { 8 | use super::Identifier; 9 | use super::providers::MemoryDisk; 10 | const MEMORY_DISK_NAME: &'static str = "boot_disk"; 11 | let mut status = ::display::text_mode::BootStatus::new("Loading boot memory disk"); 12 | 13 | let module = boot_information.module_tags() 14 | .find(|module| module.name() == MEMORY_DISK_NAME); 15 | let module = match module { 16 | Some(module) => module, 17 | None => { 18 | status.set_warning().with_message(); 19 | println!("Unable to locate boot memory disk"); 20 | return; 21 | } 22 | }; 23 | 24 | let data = load_module(module); 25 | let boot_disk = match MemoryDisk::parse_archive(&data) { 26 | Some(boot_disk) => boot_disk, 27 | None => { 28 | status.set_failure().with_message(); 29 | eprintln!("Failed to parse boot memory disk image"); 30 | return; 31 | } 32 | }; 33 | ROOT_PROVIDER.set(Root::new()); 34 | ROOT_PROVIDER.lock().mount(Identifier::new("boot_disk"), box boot_disk); 35 | } 36 | 37 | // Copies the module data into a heap allocated Vec so it 38 | // still exists once the kernel finishes booting 39 | pub fn load_module(module: &::multiboot2::ModuleTag) -> Vec { 40 | use memory::FRAME_ALLOCATOR; 41 | use memory::PhysicalAddress; 42 | use memory::HugeFrame; 43 | use memory::FrameLike; 44 | use paging::HugePage; 45 | use paging::EntryFlags; 46 | use paging::PageLike; 47 | use paging::ACTIVE_PAGE_TABLE; 48 | use core::ops::DerefMut; 49 | 50 | // We use huge frames here because the boot disk can get really large 51 | let start = HugeFrame::from_address(PhysicalAddress::new(module.start_address() as u64)); 52 | let end = HugeFrame::from_address(PhysicalAddress::new(module.end_address() as u64)); 53 | let page = HugePage::from_address(::paging::reserved::HUGE_TEMPORARY_PAGE); 54 | 55 | let mut data = Vec::new(); 56 | for frame in ::memory::FrameIter::inclusive(start, end) { 57 | let mut current = frame.start_address().raw().max(module.start_address() as u64); 58 | let end = frame.end_address().raw().min(module.end_address() as u64); 59 | 60 | // Temporarily map the module data frame into a page 61 | ACTIVE_PAGE_TABLE.lock().map_to(page.clone(), frame, EntryFlags::empty(), FRAME_ALLOCATOR.lock().deref_mut()); 62 | 63 | // Copy the data via the temporary page 64 | while current <= end { 65 | let byte = ((current % HugePage::SIZE) + page.start_address().raw() as u64) as *const u8; 66 | data.push(unsafe { *byte }); 67 | current += 1; 68 | } 69 | 70 | // Un map the page so we can use it again in the next iteration 71 | ACTIVE_PAGE_TABLE.lock().discard(page.clone(), FRAME_ALLOCATOR.lock().deref_mut()); 72 | } 73 | data 74 | } 75 | -------------------------------------------------------------------------------- /kernel/src/graph/location.rs: -------------------------------------------------------------------------------- 1 | use alloc::String; 2 | use alloc::Vec; 3 | 4 | pub struct Location { 5 | path: Vec, 6 | } 7 | 8 | impl Location { 9 | pub fn new(path: Vec) -> Location { 10 | Location { 11 | path, 12 | } 13 | } 14 | 15 | pub fn parse(string: &str) -> Location { 16 | let segments = string.split('/') 17 | .map(|string| Identifier::new(string)) 18 | .collect(); 19 | Self::new(segments) 20 | } 21 | 22 | pub fn as_slice(&self) -> LocationSlice { 23 | LocationSlice { 24 | path: &self.path, 25 | } 26 | } 27 | } 28 | 29 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] 30 | pub struct Identifier { 31 | identifier: String, 32 | } 33 | 34 | impl Identifier { 35 | pub fn new(identifier: S) -> Identifier { 36 | Identifier { 37 | identifier: identifier.to_string(), 38 | } 39 | } 40 | } 41 | 42 | pub struct LocationSlice<'a> { 43 | path: &'a [Identifier], 44 | } 45 | 46 | impl<'a> LocationSlice<'a> { 47 | pub fn try_last(&self) -> Option<&'a Identifier> { 48 | if self.path.len() == 1 { 49 | self.path.last() 50 | } else { 51 | None 52 | } 53 | } 54 | 55 | pub fn split(&self) -> Option<(&'a Identifier, LocationSlice<'a>)> { 56 | let (first, rest) = self.path.split_first()?; 57 | Some((first, LocationSlice { path: rest })) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kernel/src/graph/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::functions::ROOT_PROVIDER; 2 | pub use self::location::Identifier; 3 | pub use self::location::Location; 4 | pub use self::location::LocationSlice; 5 | pub use self::provider::Provider; 6 | pub use self::resource::Resource; 7 | pub use self::resource::ResourceError; 8 | pub use self::resource::ResourceResult; 9 | 10 | pub mod resource; 11 | pub mod resources; 12 | pub mod provider; 13 | pub mod providers; 14 | pub mod location; 15 | pub mod functions; 16 | -------------------------------------------------------------------------------- /kernel/src/graph/provider.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use super::LocationSlice; 3 | use super::Resource; 4 | 5 | pub trait Provider { 6 | fn open(&mut self, location: &LocationSlice) -> Option>; 7 | } 8 | -------------------------------------------------------------------------------- /kernel/src/graph/providers/memory_disk.rs: -------------------------------------------------------------------------------- 1 | use alloc::arc::Arc; 2 | use alloc::boxed::Box; 3 | use alloc::BTreeMap; 4 | use alloc::String; 5 | use alloc::string::ToString; 6 | use alloc::Vec; 7 | use graph::*; 8 | use graph::resources::MemoryFile; 9 | use spin::RwLock; 10 | 11 | pub struct MemoryDisk { 12 | files: BTreeMap>>>, 13 | folders: BTreeMap, 14 | } 15 | 16 | impl MemoryDisk { 17 | const SECTOR_SIZE: usize = 512; 18 | 19 | pub fn new() -> MemoryDisk { 20 | MemoryDisk { 21 | files: BTreeMap::new(), 22 | folders: BTreeMap::new(), 23 | } 24 | } 25 | 26 | // The boot disk data is in the format of a tar archive 27 | // The tar archive uses a file system called USTAR 28 | // See https://wiki.osdev.org/USTAR 29 | pub fn parse_archive(archive_data: &[u8]) -> Option { 30 | let mut memory_disk = Self::new(); 31 | 32 | let mut cursor = 0; 33 | while cursor <= (archive_data.len() - Self::SECTOR_SIZE) { 34 | let (file_path, data, new_cursor) = match Self::parse_file(cursor, archive_data) { 35 | Some(data) => data, 36 | None => break, 37 | }; 38 | 39 | let location = Location::parse(&file_path[2..]); 40 | Self::add_file(&mut memory_disk, &location.as_slice(), data); 41 | 42 | // Align up because the file may not be a multiple of 512 bytes 43 | cursor = ::utility::math::align_up_usize(new_cursor, Self::SECTOR_SIZE); 44 | } 45 | 46 | Some(memory_disk) 47 | } 48 | 49 | fn add_file(current: &mut MemoryDisk, path: &LocationSlice, data: Vec) { 50 | if let Some(last) = path.try_last() { 51 | let data = Arc::new(RwLock::new(data)); 52 | current.files.insert(last.clone(), data); 53 | return; 54 | } 55 | 56 | let (first, rest) = path.split().unwrap(); 57 | let next = match current.folders.get_mut(first) { 58 | Some(next) => return Self::add_file(next, &rest, data), 59 | None => { 60 | let mut next = MemoryDisk::new(); 61 | Self::add_file(&mut next, &rest, data); 62 | next 63 | } 64 | }; 65 | current.folders.insert(first.clone(), next); 66 | } 67 | 68 | fn parse_file(cursor: usize, archive_data: &[u8]) -> Option<(String, Vec, usize)> { 69 | if &archive_data[cursor + 257..cursor + 257 + 5] != b"ustar" { return None; } 70 | let file_path = Self::parse_file_path(cursor, archive_data)?; 71 | let file_size = Self::parse_file_size(cursor, archive_data)?; 72 | 73 | let file_data_start = cursor + Self::SECTOR_SIZE; 74 | let file_data_end = file_data_start + file_size; 75 | let file_data = archive_data[file_data_start..file_data_end].to_vec(); 76 | Some((file_path, file_data, file_data_end)) 77 | } 78 | 79 | fn parse_file_path(cursor: usize, archive_data: &[u8]) -> Option { 80 | let file_path = archive_data[cursor..cursor + 100].to_vec(); 81 | let file_path = String::from_utf8(file_path).ok()?; 82 | Some(file_path.trim_matches(0 as char).to_string()) 83 | } 84 | 85 | fn parse_file_size(cursor: usize, archive_data: &[u8]) -> Option { 86 | let file_size_octal = archive_data[cursor + 0x7c..cursor + 0x7c + 11].to_vec(); 87 | let file_size_octal = String::from_utf8(file_size_octal).ok()?; 88 | usize::from_str_radix(&file_size_octal, 8).ok() 89 | } 90 | } 91 | 92 | impl Provider for MemoryDisk { 93 | fn open(&mut self, location: &LocationSlice) -> Option> { 94 | if let Some(last) = location.try_last() { 95 | let resource = self.files.get(last)?.clone(); 96 | return Some(box MemoryFile::new(resource)); 97 | } 98 | 99 | let (first, rest) = location.split()?; 100 | self.folders.get_mut(first)?.open(&rest) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /kernel/src/graph/providers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::memory_disk::MemoryDisk; 2 | pub use self::root::Root; 3 | 4 | pub mod root; 5 | pub mod memory_disk; 6 | -------------------------------------------------------------------------------- /kernel/src/graph/providers/root.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use alloc::BTreeMap; 3 | use graph::*; 4 | 5 | pub struct Root { 6 | providers: BTreeMap>, 7 | } 8 | 9 | impl Root { 10 | pub fn new() -> Root { 11 | Root { 12 | providers: BTreeMap::new(), 13 | } 14 | } 15 | 16 | pub fn mount(&mut self, identifier: Identifier, provider: Box) { 17 | self.providers.insert(identifier, provider); 18 | } 19 | } 20 | 21 | impl Provider for Root { 22 | fn open(&mut self, location: &LocationSlice) -> Option> { 23 | let (first, rest) = location.split()?; 24 | self.providers.get_mut(first)?.open(&rest) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /kernel/src/graph/resource.rs: -------------------------------------------------------------------------------- 1 | pub type ResourceResult = Result; 2 | 3 | pub trait Resource { 4 | fn read(&mut self, buffer: &mut [u8]) -> ResourceResult; 5 | fn write(&mut self, buffer: &[u8]) -> ResourceResult<()>; 6 | fn seek(&mut self, count: usize) -> ResourceResult; 7 | fn close(&mut self) -> ResourceResult<()>; 8 | } 9 | 10 | impl Resource { 11 | pub fn read_all(&mut self) -> ::alloc::Vec { 12 | let mut data = ::alloc::Vec::new(); 13 | let mut buffer = [0]; 14 | while let Ok(count) = self.read(&mut buffer) { 15 | if count == 0 { break; } 16 | data.push(buffer[0]); 17 | } 18 | data 19 | } 20 | } 21 | 22 | #[derive(Debug)] 23 | pub enum ResourceError { 24 | Closed, 25 | } 26 | -------------------------------------------------------------------------------- /kernel/src/graph/resources/memory_file.rs: -------------------------------------------------------------------------------- 1 | use alloc::arc::Arc; 2 | use alloc::Vec; 3 | use graph::resource::*; 4 | use spin::RwLock; 5 | 6 | pub struct MemoryFile { 7 | data: Option>>>, 8 | position: usize, 9 | } 10 | 11 | impl MemoryFile { 12 | pub fn new(data: Arc>>) -> MemoryFile { 13 | MemoryFile { 14 | data: Some(data), 15 | position: 0, 16 | } 17 | } 18 | } 19 | 20 | impl Resource for MemoryFile { 21 | fn read(&mut self, buffer: &mut [u8]) -> ResourceResult { 22 | let data = self.data.as_ref().ok_or(ResourceError::Closed)?.read(); 23 | let current_position = self.position; 24 | 25 | for byte in buffer { 26 | let data_byte = data.get(self.position); 27 | match data_byte { 28 | Some(data_byte) => { 29 | *byte = *data_byte; 30 | self.position += 1; 31 | } 32 | None => break, 33 | } 34 | } 35 | Ok(self.position - current_position) 36 | } 37 | 38 | fn write(&mut self, buffer: &[u8]) -> ResourceResult<()> { 39 | let mut data = self.data.as_mut().ok_or(ResourceError::Closed)?.write(); 40 | for byte in buffer { 41 | if self.position == data.len() { 42 | data.push(*byte); 43 | } else { 44 | data[self.position] = *byte; 45 | } 46 | self.position += 1; 47 | } 48 | Ok(()) 49 | } 50 | 51 | fn seek(&mut self, count: usize) -> ResourceResult { 52 | let data = self.data.as_ref().ok_or(ResourceError::Closed)?.read(); 53 | let current_position = self.position; 54 | 55 | self.position += count; 56 | if self.position >= data.len() { 57 | self.position = data.len() - 1; 58 | } 59 | Ok(self.position - current_position) 60 | } 61 | 62 | fn close(&mut self) -> ResourceResult<()> { 63 | let _ = self.data.take(); 64 | Ok(()) 65 | } 66 | } 67 | 68 | impl Drop for MemoryFile { 69 | fn drop(&mut self) { 70 | let _ = self.close(); 71 | } 72 | } -------------------------------------------------------------------------------- /kernel/src/graph/resources/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::memory_file::MemoryFile; 2 | 3 | pub mod memory_file; -------------------------------------------------------------------------------- /kernel/src/interrupts/functions.rs: -------------------------------------------------------------------------------- 1 | use spin::Once; 2 | use structures::FixedStack; 3 | use super::GdtDescriptor; 4 | use utility::Global; 5 | use x86_64::structures::gdt::SegmentSelector; 6 | use x86_64::structures::idt::Idt; 7 | use x86_64::structures::tss::TaskStateSegment; 8 | 9 | static GDT: Once = Once::new(); 10 | static IDT: Once = Once::new(); 11 | pub static TSS: Global = Global::new("TASK_STATE_SEGMENT"); 12 | 13 | // These user selectors are used when creating an initial 14 | // user mode stack. See task/loaders/stack::create_initial_stack 15 | pub static USER_CODE_SELECTOR: Once = Once::new(); 16 | pub static USER_DATA_SELECTOR: Once = Once::new(); 17 | 18 | // These stacks must be marked as mutable so they are placed 19 | // in the .bss segment. Otherwise, it would cause a page fault 20 | // because it would be in the .rodata section. 21 | static mut DOUBLE_FAULT_STACK: FixedStack = FixedStack::new(); 22 | static mut PAGE_FAULT_STACK: FixedStack = FixedStack::new(); 23 | static mut GENERAL_FAULT_STACK: FixedStack = FixedStack::new(); 24 | 25 | const DOUBLE_FAULT_STACK_INDEX: usize = 0; 26 | const PAGE_FAULT_STACK_INDEX: usize = 2; 27 | const GENERAL_FAULT_STACK_INDEX: usize = 3; 28 | 29 | const TIMER_INTERRUPT_INDEX: usize = 0; 30 | const KEYBOARD_INTERRUPT_INDEX: usize = 1; 31 | 32 | // We have shifted the interrupt vectors up 32 so the actual 33 | // index in the interrupt table is 0xaa - 32 34 | const SYSTEM_CALL_INDEX: usize = 0xaa - super::pic_functions::PIC_ONE_VECTOR_BASE as usize; 35 | 36 | pub fn initialize() { 37 | let _status = ::display::text_mode::BootStatus::new("Initializing interrupt descriptor table"); 38 | initialize_task_state_segment(); 39 | initialize_global_descriptor_table(); 40 | initialize_interrupt_table(); 41 | } 42 | 43 | pub fn post_initialize() { 44 | let _status = ::display::text_mode::BootStatus::new("Enabling interrupts"); 45 | super::pic_functions::initialize(); 46 | super::pit_functions::initialize(); 47 | 48 | // Enabling interrupts allows timer interrupts to be 49 | // fired and handled. 50 | unsafe { ::x86_64::instructions::interrupts::enable(); } 51 | } 52 | 53 | fn initialize_global_descriptor_table() { 54 | use core::ops::Deref; 55 | 56 | let mut kernel_code_selector = SegmentSelector(0); 57 | let mut kernel_data_selector = SegmentSelector(0); 58 | let mut user_code_selector = SegmentSelector(0); 59 | let mut user_data_selector = SegmentSelector(0); 60 | let mut tss_selector = SegmentSelector(0); 61 | 62 | let gdt = GDT.call_once(|| { 63 | let mut gdt = super::Gdt::new(); 64 | // Descriptors are needed for both kernel mode 65 | // and user mode. See GdtDescriptor 66 | kernel_code_selector = gdt.add_kernel_entry(GdtDescriptor::kernel_code_segment()); 67 | kernel_data_selector = gdt.add_kernel_entry(GdtDescriptor::kernel_data_segment()); 68 | user_code_selector = gdt.add_user_entry(GdtDescriptor::user_code_segment()); 69 | user_data_selector = gdt.add_user_entry(GdtDescriptor::user_data_segment()); 70 | tss_selector = gdt.add_kernel_entry(GdtDescriptor::tss_segment(&TSS.lock().deref())); 71 | gdt 72 | }); 73 | gdt.load(); 74 | 75 | USER_CODE_SELECTOR.call_once(|| user_code_selector.0); 76 | USER_DATA_SELECTOR.call_once(|| user_data_selector.0); 77 | 78 | unsafe { 79 | ::x86_64::instructions::segmentation::set_cs(kernel_code_selector); 80 | ::x86_64::instructions::segmentation::load_ds(kernel_data_selector); 81 | ::x86_64::instructions::tables::load_tss(tss_selector); 82 | } 83 | } 84 | 85 | fn initialize_task_state_segment() { 86 | use x86_64::VirtualAddress; 87 | let mut tss = TaskStateSegment::new(); 88 | unsafe { 89 | // The TaskStateSegment can store up to seven stacks 90 | // These stacks are switched to when interrupts handlers 91 | // are called 92 | tss.interrupt_stack_table[DOUBLE_FAULT_STACK_INDEX] = VirtualAddress(DOUBLE_FAULT_STACK.address()); 93 | tss.interrupt_stack_table[PAGE_FAULT_STACK_INDEX] = VirtualAddress(PAGE_FAULT_STACK.address()); 94 | tss.interrupt_stack_table[GENERAL_FAULT_STACK_INDEX] = VirtualAddress(GENERAL_FAULT_STACK.address()); 95 | } 96 | TSS.set(tss); 97 | } 98 | 99 | fn initialize_interrupt_table() { 100 | let mut table = Idt::new(); 101 | unsafe { set_interrupt_handlers(&mut table); } 102 | IDT.call_once(|| table).load(); 103 | } 104 | 105 | unsafe fn set_interrupt_handlers(table: &mut Idt) { 106 | use super::handlers::*; 107 | use x86_64::PrivilegeLevel; 108 | 109 | // Note: By default, interrupts are automatically disabled 110 | // by the processor when a interrupt handler is called 111 | // and enabled when the handler returns 112 | table.divide_by_zero.set_handler_fn(zero_divide_handler); 113 | table.double_fault.set_handler_fn(double_fault_handler) 114 | .set_stack_index(DOUBLE_FAULT_STACK_INDEX as u16); 115 | table.breakpoint.set_handler_fn(breakpoint_handler); 116 | table.page_fault.set_handler_fn(page_fault_handler) 117 | .set_stack_index(PAGE_FAULT_STACK_INDEX as u16); 118 | table.general_protection_fault.set_handler_fn(general_fault_handler) 119 | .set_stack_index(GENERAL_FAULT_STACK_INDEX as u16); 120 | table.invalid_opcode.set_handler_fn(invalid_opcode_handler); 121 | table.interrupts[TIMER_INTERRUPT_INDEX].set_handler_fn(timer_handler); 122 | table.interrupts[KEYBOARD_INTERRUPT_INDEX].set_handler_fn(keyboard_handler); 123 | 124 | // We allow interrupts so the scheduler can preempt a system call 125 | // We need the privilege level to be Ring3 so user mode 126 | // threads can use 127 | // 128 | // int 0xaa 129 | // 130 | // without causing a General Protection Fault. 131 | // 132 | // A distinction between an "interrupt" and a "trap" should be 133 | // made here. Traps are almost exactly the same as interrupts 134 | // but they allow interrupts to occur while their handler is 135 | // executing. In this case, the system call handler would be 136 | // considered a trap. 137 | table.interrupts[SYSTEM_CALL_INDEX] 138 | .set_handler_fn(system_call_handler) 139 | .disable_interrupts(false) 140 | .set_privilege_level(PrivilegeLevel::Ring3); 141 | } 142 | -------------------------------------------------------------------------------- /kernel/src/interrupts/gdt.rs: -------------------------------------------------------------------------------- 1 | use super::GdtDescriptor; 2 | use x86_64::structures::gdt::SegmentSelector; 3 | 4 | pub struct Gdt { 5 | // The GDT can have up to 8192 entries but since we don't 6 | // use segmentation, we shouldn't need anymore than 64 bytes worth 7 | // of entries 8 | table: [u64; 8], 9 | next_free_index: usize, 10 | } 11 | 12 | impl Gdt { 13 | pub const fn new() -> Gdt { 14 | Gdt { 15 | table: [0; 8], 16 | 17 | // The first entry is called the Null Descriptor and it should be 18 | // left zeroed. The processor General Protection Faults when 19 | // the first entry of GDT is referenced, to protect against 20 | // applications that accidentally use the null descriptor. 21 | next_free_index: 1, 22 | } 23 | } 24 | 25 | pub fn load(&'static self) { 26 | use x86_64::instructions::tables::{DescriptorTablePointer, lgdt}; 27 | use core::mem::size_of; 28 | 29 | let pointer = DescriptorTablePointer { 30 | base: self.table.as_ptr() as u64, 31 | limit: (self.table.len() * size_of::() - 1) as u16, 32 | }; 33 | 34 | unsafe { lgdt(&pointer) }; 35 | } 36 | 37 | pub fn add_kernel_entry(&mut self, entry: GdtDescriptor) -> SegmentSelector { 38 | let index = self.create_entry(entry); 39 | SegmentSelector::new(index, ::x86_64::PrivilegeLevel::Ring0) 40 | } 41 | 42 | pub fn add_user_entry(&mut self, entry: GdtDescriptor) -> SegmentSelector { 43 | let index = self.create_entry(entry); 44 | SegmentSelector::new(index, ::x86_64::PrivilegeLevel::Ring3) 45 | } 46 | 47 | fn create_entry(&mut self, entry: GdtDescriptor) -> u16 { 48 | match entry { 49 | GdtDescriptor::UserSegment(value) => self.push_value(value) as u16, 50 | GdtDescriptor::SystemSegment(value_low, value_high) => { 51 | let index = self.push_value(value_low); 52 | self.push_value(value_high); 53 | index as u16 54 | } 55 | } 56 | } 57 | 58 | fn push_value(&mut self, value: u64) -> usize { 59 | if self.next_free_index < self.table.len() { 60 | let index = self.next_free_index; 61 | self.table[index] = value; 62 | self.next_free_index += 1; 63 | index 64 | } else { 65 | panic!("GDT is full"); 66 | } 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /kernel/src/interrupts/gdt_descriptor.rs: -------------------------------------------------------------------------------- 1 | use x86_64::structures::tss::TaskStateSegment; 2 | 3 | // GDT descriptors describe linear regions of memory and 4 | // the rules that govern them. In long mode, descriptors 5 | // are rarely used anymore but they are still required 6 | // (due to paging replacing segmentation). Instead of 7 | // governing a region of memory, we just have them govern 8 | // the entire address space. 9 | 10 | pub enum GdtDescriptor { 11 | UserSegment(u64), 12 | SystemSegment(u64, u64), 13 | } 14 | 15 | impl GdtDescriptor { 16 | pub fn kernel_code_segment() -> GdtDescriptor { 17 | let flags = DescriptorFlags::USER_SEGMENT | DescriptorFlags::PRESENT | 18 | DescriptorFlags::EXECUTABLE | DescriptorFlags::LONG_MODE; 19 | GdtDescriptor::UserSegment(flags.bits()) 20 | } 21 | 22 | pub fn kernel_data_segment() -> GdtDescriptor { 23 | let flags = DescriptorFlags::USER_SEGMENT | DescriptorFlags::PRESENT | 24 | DescriptorFlags::LONG_MODE; 25 | GdtDescriptor::UserSegment(flags.bits()) 26 | } 27 | 28 | pub fn user_code_segment() -> GdtDescriptor { 29 | let flags = DescriptorFlags::USER_SEGMENT | DescriptorFlags::PRESENT | 30 | DescriptorFlags::EXECUTABLE | DescriptorFlags::LONG_MODE | DescriptorFlags::RING_USER; 31 | GdtDescriptor::UserSegment(flags.bits()) 32 | } 33 | 34 | pub fn user_data_segment() -> GdtDescriptor { 35 | let flags = DescriptorFlags::USER_SEGMENT | DescriptorFlags::PRESENT | 36 | DescriptorFlags::LONG_MODE | DescriptorFlags::WRITABLE | DescriptorFlags::RING_USER; 37 | GdtDescriptor::UserSegment(flags.bits()) 38 | } 39 | 40 | pub fn tss_segment(tss: &TaskStateSegment) -> GdtDescriptor { 41 | use core::mem::size_of; 42 | use bit_field::BitField; 43 | let tss_pointer = tss as *const _ as u64; 44 | 45 | let mut low = DescriptorFlags::PRESENT.bits(); 46 | low.set_bits(16..40, tss_pointer.get_bits(0..24)); 47 | low.set_bits(56..64, tss_pointer.get_bits(24..32)); 48 | low.set_bits(0..16, (size_of::() - 1) as u64); 49 | low.set_bits(40..44, 0b1001); 50 | 51 | let mut high = 0; 52 | high.set_bits(0..32, tss_pointer.get_bits(32..64)); 53 | GdtDescriptor::SystemSegment(low, high) 54 | } 55 | } 56 | 57 | bitflags! { 58 | struct DescriptorFlags: u64 { 59 | const WRITABLE = 1 << 41; 60 | const CONFORMING = 1 << 42; 61 | const EXECUTABLE = 1 << 43; 62 | const USER_SEGMENT = 1 << 44; 63 | const RING_USER = 3 << 45; 64 | const PRESENT = 1 << 47; 65 | const LONG_MODE = 1 << 53; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kernel/src/interrupts/handlers.rs: -------------------------------------------------------------------------------- 1 | use super::send_interrupt_end; 2 | use x86_64::structures::idt::ExceptionStackFrame; 3 | use x86_64::structures::idt::PageFaultErrorCode; 4 | 5 | pub extern "x86-interrupt" fn zero_divide_handler(stack_frame: &mut ExceptionStackFrame) { 6 | panic!("\nDivide by zero: {:#?}", stack_frame); 7 | } 8 | 9 | pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut ExceptionStackFrame) { 10 | panic!("\nBreakpoint: {:#?}", stack_frame); 11 | } 12 | 13 | pub extern "x86-interrupt" fn double_fault_handler(stack_frame: &mut ExceptionStackFrame, _error_code: u64) { 14 | panic!("\nDouble Fault: {:#?}", stack_frame); 15 | } 16 | 17 | pub extern "x86-interrupt" fn page_fault_handler(stack_frame: &mut ExceptionStackFrame, error_code: PageFaultErrorCode) { 18 | // The cr2 register contains the address that caused the 19 | // page fault when a page fault occurs 20 | let address = ::x86_64::registers::control_regs::cr2(); 21 | let address = ::paging::VirtualAddress::new(address.0); 22 | if !::memory::functions::handle_heap_fault(address, &error_code) { 23 | panic!("\nPage Fault: {:#?}\n{:#?}", error_code, stack_frame); 24 | } 25 | } 26 | 27 | pub extern "x86-interrupt" fn general_fault_handler(stack_frame: &mut ExceptionStackFrame, _error_code: u64) { 28 | panic!("\nGeneral Protection Fault: {:#?}", stack_frame); 29 | } 30 | 31 | pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: &mut ExceptionStackFrame) { 32 | panic!("\nInvalid Opcode Fault: {:#?}", stack_frame); 33 | } 34 | 35 | pub extern "x86-interrupt" fn keyboard_handler(_stack_frame: &mut ExceptionStackFrame) { 36 | // Note: This handler isn't actually used because we've 37 | // disabled the keyboard interrupt. See interrupts::pic_functions 38 | if let Some(key_code) = ::keyboard::SYSTEM_KEYBOARD.lock().parse_port_input() { 39 | ::shell::SYSTEM_SHELL.lock().on_key_press(key_code); 40 | } 41 | send_interrupt_end(false); 42 | } 43 | 44 | pub extern "x86-interrupt" fn timer_handler(_stack_frame: &mut ExceptionStackFrame) { 45 | const TASK_SWITCH_STACK_TOP: usize = ::paging::reserved::TASK_SWITCH_STACK_TOP.raw(); 46 | // All the registers are pushed here 47 | unsafe { 48 | // First, we save the current stack pointer so we can store it in the scheduler later. 49 | // We also switch stacks because if we don't, when we change page tables 50 | // the stack will not longer be valid, which causes a loop of page faults. 51 | // The first argument to a function is stored in the rdi register 52 | asm!("mov rdi, rsp 53 | mov rsp, $0 54 | call $1 55 | mov rsp, rax" 56 | :: "i"(TASK_SWITCH_STACK_TOP), // i indicates that the argument is a constant 57 | "i"(::task::functions::context_switch as extern "C" fn(usize) -> usize) 58 | : "rax","rbx","rcx","rdx","rbp","rsi","rdi","r8","r9","r10","r11","r12","r13","r14","r15" 59 | : "intel"); 60 | // All registers are clobbered to force LLVM to push and pop all the registers 61 | // onto the stack, thus saving them. 62 | // If the user mode threads use floating operations too, then 63 | // the floating point registers need to be pushed and popped as well 64 | // 65 | // Note: When the context switch happens, all the registers are still on the stack 66 | // and are popped when the context switch comes back to this stack 67 | } 68 | // All the registers are popped here 69 | } 70 | 71 | pub extern "x86-interrupt" fn system_call_handler(_stack_frame: &mut ExceptionStackFrame) { 72 | let code; 73 | // TODO create real system call handler 74 | unsafe { 75 | // The number in r15 is moved into the `code` variable 76 | asm!("mov rbx, r15" : "={rbx}"(code) :: "rbx", "r15" : "intel"); 77 | } 78 | ::system_call::functions::system_call_hook(code); 79 | } 80 | -------------------------------------------------------------------------------- /kernel/src/interrupts/mod.rs: -------------------------------------------------------------------------------- 1 | use self::gdt::Gdt; 2 | use self::gdt_descriptor::GdtDescriptor; 3 | pub use self::pic_functions::send_interrupt_end; 4 | 5 | pub mod functions; 6 | pub mod handlers; 7 | pub mod gdt; 8 | pub mod gdt_descriptor; 9 | pub mod pic_functions; 10 | pub mod pit_functions; 11 | -------------------------------------------------------------------------------- /kernel/src/interrupts/pic_functions.rs: -------------------------------------------------------------------------------- 1 | use x86_64::instructions::port::outb; 2 | 3 | pub const PIC_ONE_VECTOR_BASE: u8 = 32; 4 | pub const PIC_TWO_VECTOR_BASE: u8 = 40; 5 | 6 | const PIC_ONE_COMMAND_PORT: u16 = 0x20; 7 | const PIC_TWO_COMMAND_PORT: u16 = 0xa0; 8 | 9 | const PIC_ONE_DATA_PORT: u16 = 0x21; 10 | const PIC_TWO_DATA_PORT: u16 = 0xa1; 11 | 12 | pub fn initialize() { 13 | unsafe { 14 | remap_pic(); 15 | mask_pic(); 16 | } 17 | } 18 | 19 | unsafe fn remap_pic() { 20 | // Remapping the PIC is very important because by default 21 | // the interrupt vectors are mapped to 0 - 31. However, 22 | // interrupts at these vectors overlap the exception vectors 23 | // that the processor uses. That means, when the timer 24 | // interrupt is fired, the processor detects this as a 25 | // Coprocessor Segment Overrun exception. To fix this, 26 | // we remap the interrupt vectors above 31. 27 | 28 | use x86_64::instructions::port::outb; 29 | const PIC_RESTART_COMMAND: u8 = 0x11; 30 | 31 | outb(PIC_ONE_COMMAND_PORT, PIC_RESTART_COMMAND); 32 | outb(PIC_TWO_COMMAND_PORT, PIC_RESTART_COMMAND); 33 | 34 | outb(PIC_ONE_DATA_PORT, PIC_ONE_VECTOR_BASE); 35 | outb(PIC_TWO_DATA_PORT, PIC_TWO_VECTOR_BASE); 36 | 37 | outb(PIC_ONE_DATA_PORT, 0x04); 38 | outb(PIC_TWO_DATA_PORT, 0x02); 39 | 40 | outb(PIC_ONE_DATA_PORT, 0x01); 41 | outb(PIC_ONE_DATA_PORT, 0x01); 42 | } 43 | 44 | unsafe fn mask_pic() { 45 | // A bit is 0 when we want that interrupt to be 46 | // enabled. In this case, we enable the timer interrupt 47 | // only, for scheduling. 48 | outb(PIC_ONE_DATA_PORT, 0b1111_1110); 49 | outb(PIC_TWO_DATA_PORT, 0b1111_1111); 50 | } 51 | 52 | pub fn send_interrupt_end(both_chips: bool) { 53 | // Some interrupts require you to send an "interrupt end" signal 54 | // to signal that you have finished servicing the interrupt. 55 | // If this is not done, no more interrupts will fire 56 | 57 | unsafe { 58 | if both_chips { 59 | outb(PIC_TWO_COMMAND_PORT, 0x20); 60 | } 61 | outb(PIC_ONE_COMMAND_PORT, 0x20); 62 | } 63 | } -------------------------------------------------------------------------------- /kernel/src/interrupts/pit_functions.rs: -------------------------------------------------------------------------------- 1 | const BASE_FREQUENCY: u32 = 1_193_180; 2 | const COMMAND_PORT: u16 = 0x43; 3 | const DATA_PORT: u16 = 0x40; 4 | 5 | pub fn initialize() { 6 | const DIVIDER_FREQUENCY: u32 = 100; 7 | unsafe { 8 | set_frequency(DIVIDER_FREQUENCY); 9 | } 10 | } 11 | 12 | unsafe fn set_frequency(hertz: u32) { 13 | use x86_64::instructions::port::outb; 14 | 15 | let division = BASE_FREQUENCY / hertz; 16 | let mode = 0b0011_0110; 17 | 18 | let low = division & 0x0000_00ff; 19 | let high = (division & 0x0000_ff00) >> 8; 20 | 21 | outb(COMMAND_PORT, mode); 22 | outb(DATA_PORT, low as u8); 23 | outb(DATA_PORT, high as u8); 24 | } 25 | -------------------------------------------------------------------------------- /kernel/src/keyboard/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::ps2_driver::PS2Driver; 2 | 3 | pub mod ps2_driver; -------------------------------------------------------------------------------- /kernel/src/keyboard/functions.rs: -------------------------------------------------------------------------------- 1 | use ::spin::Mutex; 2 | use super::drivers::PS2Driver; 3 | 4 | pub static SYSTEM_KEYBOARD: Mutex = Mutex::new(PS2Driver::new()); -------------------------------------------------------------------------------- /kernel/src/keyboard/key_event.rs: -------------------------------------------------------------------------------- 1 | use super::key_other::*; 2 | use super::key_printable::*; 3 | 4 | #[derive(Debug)] 5 | pub struct KeyEvent { 6 | pub key_code: KeyCode, 7 | pub state: KeyState, 8 | } 9 | 10 | #[derive(Debug, Copy, Clone, PartialEq)] 11 | pub enum KeyState { 12 | Pressed, 13 | Released, 14 | } 15 | 16 | #[derive(Debug)] 17 | pub enum KeyCode { 18 | Character(KeyCharacter), 19 | Number(KeyNumber), 20 | Function(KeyFunction), 21 | KeyPad(KeyPad), 22 | Symbol(KeySymbol), 23 | Modifier(KeyModifier), 24 | Special(KeySpecial), 25 | Multimedia(KeyMultimedia), 26 | Arrow(KeyArrow), 27 | } 28 | -------------------------------------------------------------------------------- /kernel/src/keyboard/key_other.rs: -------------------------------------------------------------------------------- 1 | use core::convert::Into; 2 | use super::key_printable::KeyNumber; 3 | use super::KeyCode; 4 | 5 | #[derive(Debug)] 6 | pub enum KeyFunction { 7 | One, 8 | Two, 9 | Three, 10 | Four, 11 | Five, 12 | Six, 13 | Seven, 14 | Eight, 15 | Nine, 16 | Ten, 17 | Eleven, 18 | Twelve, 19 | } 20 | 21 | impl Into for KeyFunction { 22 | fn into(self) -> KeyCode { 23 | KeyCode::Function(self) 24 | } 25 | } 26 | 27 | #[derive(Debug)] 28 | pub enum KeyPad { 29 | Number(KeyNumber), 30 | Asterisk, 31 | Minus, 32 | Plus, 33 | Dot, 34 | Enter, 35 | SlashForward, 36 | } 37 | 38 | impl Into for KeyPad { 39 | fn into(self) -> KeyCode { 40 | KeyCode::KeyPad(self) 41 | } 42 | } 43 | 44 | #[derive(Debug)] 45 | pub enum KeyModifier { 46 | ControlLeft, 47 | ControlRight, 48 | AltLeft, 49 | AltRight, 50 | ShiftLeft, 51 | ShiftRight, 52 | SuperLeft, 53 | SuperRight, 54 | } 55 | 56 | impl Into for KeyModifier { 57 | fn into(self) -> KeyCode { 58 | KeyCode::Modifier(self) 59 | } 60 | } 61 | 62 | #[derive(Debug)] 63 | pub enum KeySpecial { 64 | Backspace, 65 | CapsLock, 66 | Tab, 67 | PageUp, 68 | PageDown, 69 | Escape, 70 | Enter, 71 | Home, 72 | End, 73 | Insert, 74 | Delete, 75 | Pause, 76 | PrintScreen, 77 | Menu, 78 | Power, 79 | Sleep, 80 | Wake, 81 | NumberLock, 82 | ScrollLock, 83 | } 84 | 85 | impl Into for KeySpecial { 86 | fn into(self) -> KeyCode { 87 | KeyCode::Special(self) 88 | } 89 | } 90 | 91 | #[derive(Debug)] 92 | pub enum KeyMultimedia { 93 | Previous, 94 | Next, 95 | Mute, 96 | Calculator, 97 | Play, 98 | Stop, 99 | VolumeDown, 100 | VolumeUp, 101 | WebHome, 102 | WebSearch, 103 | WebFavourites, 104 | WebRefresh, 105 | WebStop, 106 | WebForward, 107 | WebBack, 108 | Computer, 109 | Email, 110 | MediaSelect, 111 | } 112 | 113 | impl Into for KeyMultimedia { 114 | fn into(self) -> KeyCode { 115 | KeyCode::Multimedia(self) 116 | } 117 | } 118 | 119 | #[derive(Debug)] 120 | pub enum KeyArrow { 121 | Up, 122 | Down, 123 | Left, 124 | Right, 125 | } 126 | 127 | impl Into for KeyArrow { 128 | fn into(self) -> KeyCode { 129 | KeyCode::Arrow(self) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /kernel/src/keyboard/key_printable.rs: -------------------------------------------------------------------------------- 1 | use core::convert::Into; 2 | use super::KeyCode; 3 | 4 | #[derive(Debug)] 5 | pub struct KeyCharacter { 6 | pub letter: KeyLetter, 7 | pub is_capital: bool, 8 | } 9 | 10 | impl KeyCharacter { 11 | pub fn to_char(&self) -> char { 12 | let mut character = self.letter.to_lower_case(); 13 | if self.is_capital { 14 | character = character.to_ascii_uppercase(); 15 | } 16 | character 17 | } 18 | } 19 | 20 | impl Into for KeyCharacter { 21 | fn into(self) -> KeyCode { 22 | KeyCode::Character(self) 23 | } 24 | } 25 | 26 | #[derive(Debug)] 27 | pub enum KeyLetter { 28 | A, 29 | B, 30 | C, 31 | D, 32 | E, 33 | F, 34 | G, 35 | H, 36 | I, 37 | J, 38 | K, 39 | L, 40 | M, 41 | N, 42 | O, 43 | P, 44 | Q, 45 | R, 46 | S, 47 | T, 48 | U, 49 | V, 50 | W, 51 | X, 52 | Y, 53 | Z, 54 | } 55 | 56 | impl KeyLetter { 57 | fn to_lower_case(&self) -> char { 58 | use self::KeyLetter::*; 59 | match *self { 60 | A => 'a', 61 | B => 'b', 62 | C => 'c', 63 | D => 'd', 64 | E => 'e', 65 | F => 'f', 66 | G => 'g', 67 | H => 'h', 68 | I => 'i', 69 | J => 'j', 70 | K => 'k', 71 | L => 'l', 72 | M => 'm', 73 | N => 'n', 74 | O => 'o', 75 | P => 'p', 76 | Q => 'q', 77 | R => 'r', 78 | S => 's', 79 | T => 't', 80 | U => 'u', 81 | V => 'v', 82 | W => 'w', 83 | X => 'x', 84 | Y => 'y', 85 | Z => 'z', 86 | } 87 | } 88 | } 89 | 90 | #[derive(Debug)] 91 | pub enum KeyNumber { 92 | Zero, 93 | One, 94 | Two, 95 | Three, 96 | Four, 97 | Five, 98 | Six, 99 | Seven, 100 | Eight, 101 | Nine, 102 | } 103 | 104 | impl KeyNumber { 105 | pub fn to_char(&self) -> char { 106 | use self::KeyNumber::*; 107 | match *self { 108 | Zero => '0', 109 | One => '1', 110 | Two => '2', 111 | Three => '3', 112 | Four => '4', 113 | Five => '5', 114 | Six => '6', 115 | Seven => '7', 116 | Eight => '8', 117 | Nine => '9', 118 | } 119 | } 120 | } 121 | 122 | impl Into for KeyNumber { 123 | fn into(self) -> KeyCode { 124 | KeyCode::Number(self) 125 | } 126 | } 127 | 128 | #[derive(Debug)] 129 | pub enum KeySymbol { 130 | SquareLeft, 131 | SquareRight, 132 | SlashBackward, 133 | SlashForward, 134 | AngleLeft, 135 | AngleRight, 136 | BracketLeft, 137 | BracketRight, 138 | CurlyLeft, 139 | CurlyRight, 140 | Space, 141 | Minus, 142 | Plus, 143 | Equal, 144 | Underscore, 145 | Pipe, 146 | Question, 147 | At, 148 | Exclamation, 149 | Hash, 150 | Dollar, 151 | Percentage, 152 | AngleUp, 153 | Ampersand, 154 | Asterisk, 155 | Colon, 156 | Semicolon, 157 | DoubleQuote, 158 | SingleQuote, 159 | Tilde, 160 | BackTick, 161 | Comma, 162 | Dot, 163 | } 164 | 165 | impl KeySymbol { 166 | pub fn to_char(&self) -> char { 167 | use self::KeySymbol::*; 168 | match *self { 169 | SquareLeft => '[', 170 | SquareRight => ']', 171 | SlashBackward => '\\', 172 | SlashForward => '/', 173 | AngleLeft => '<', 174 | AngleRight => '>', 175 | BracketLeft => '(', 176 | BracketRight => ')', 177 | CurlyLeft => '{', 178 | CurlyRight => '}', 179 | Space => ' ', 180 | Minus => '-', 181 | Plus => '+', 182 | Equal => '=', 183 | Underscore => '_', 184 | Pipe => '|', 185 | Question => '?', 186 | Exclamation => '!', 187 | Hash => '#', 188 | Dollar => '$', 189 | Percentage => '%', 190 | AngleUp => '^', 191 | Ampersand => '&', 192 | Asterisk => '*', 193 | Colon => ':', 194 | Semicolon => ';', 195 | DoubleQuote => '"', 196 | SingleQuote => '\'', 197 | Tilde => '~', 198 | BackTick => '`', 199 | Comma => ',', 200 | Dot => '.', 201 | At => '@', 202 | } 203 | } 204 | } 205 | 206 | impl Into for KeySymbol { 207 | fn into(self) -> KeyCode { 208 | KeyCode::Symbol(self) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /kernel/src/keyboard/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::functions::SYSTEM_KEYBOARD; 2 | pub use self::key_event::KeyCode; 3 | pub use self::key_event::KeyEvent; 4 | pub use self::key_event::KeyState; 5 | 6 | pub mod drivers; 7 | pub mod key_event; 8 | pub mod key_printable; 9 | pub mod key_other; 10 | pub mod functions; 11 | -------------------------------------------------------------------------------- /kernel/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items)] 2 | #![feature(const_fn)] 3 | #![feature(asm)] 4 | #![feature(abi_x86_interrupt)] 5 | #![feature(alloc)] 6 | #![feature(box_syntax)] 7 | #![no_std] 8 | 9 | #[macro_use] 10 | extern crate alloc; 11 | extern crate bit_field; 12 | #[macro_use] 13 | extern crate bitflags; 14 | extern crate linked_list_allocator; 15 | extern crate multiboot2; 16 | extern crate rlibc; 17 | extern crate rustc_demangle; 18 | extern crate spin; 19 | extern crate x86_64; 20 | 21 | use memory::functions::HEAP_ALLOCATOR; 22 | 23 | #[macro_use] 24 | mod display; 25 | #[cfg(test)] 26 | mod tests; 27 | mod debug; 28 | mod interrupts; 29 | mod structures; 30 | mod memory; 31 | mod paging; 32 | mod utility; 33 | mod keyboard; 34 | mod shell; 35 | mod graph; 36 | mod system_call; 37 | mod task; 38 | 39 | pub const KERNEL_BASE: u64 = 0xffff_ff00_0000_0000; 40 | 41 | // The return type is ! because we don't ever want to return 42 | // from the boot_entry 43 | #[no_mangle] 44 | pub extern "C" fn boot_entry(boot_information: usize) -> ! { 45 | // This function is called after jumping from start64_2 in boot_entry.asm 46 | 47 | let boot_structure = ::utility::MultibootStructure::new(boot_information as usize); 48 | let boot_information = boot_structure.get(); 49 | ::display::text_mode::functions::initialize(); 50 | 51 | // Creating an interrupt descriptor table early on allows us to 52 | // catch fatal exceptions such as General Protection Faults. 53 | ::interrupts::functions::initialize(); 54 | 55 | let mut boot_allocator = ::memory::functions::initialize(boot_structure.clone()); 56 | 57 | // The base_table is used for creating threads that run in user mode 58 | // It contains mappings for the kernel 59 | let base_table = ::paging::functions::initialize(&boot_information, &mut boot_allocator); 60 | ::memory::functions::post_paging_initialize(boot_allocator); 61 | ::graph::functions::load_boot_disk(&boot_information); 62 | ::debug::symbols::load_kernel_symbols(); 63 | 64 | // Converts the BootAllocator into a PostBootAllocator 65 | // that supports unlimited deallocation of frames 66 | ::memory::functions::post_initialize(&boot_information); 67 | 68 | // Prepare the scheduler for when interrupts are enabled 69 | // See task/mod.rs for loading a user mode program 70 | ::task::functions::pre_initialize(); 71 | ::task::functions::initialize(); 72 | 73 | // Note: not actually used because the keyboard interrupt 74 | // is disabled 75 | ::shell::functions::initialize(); 76 | 77 | // Enables interrupts, especially the timer interrupt 78 | ::interrupts::functions::post_initialize(); 79 | 80 | println!("Kernel boot successful"); 81 | println!("Press any key to launch the kernel shell"); 82 | 83 | // We must use a loop here because non maskable interrupts 84 | // will cause the halt instruction to be skipped and then 85 | // the processor executes random memory 86 | loop { unsafe { ::x86_64::instructions::halt(); } } 87 | } 88 | 89 | #[lang = "eh_personality"] 90 | #[cfg(not(test))] 91 | #[no_mangle] 92 | pub extern fn eh_personality() {} 93 | 94 | #[lang = "panic_impl"] 95 | #[cfg(not(test))] 96 | #[no_mangle] 97 | pub extern fn kernel_panic(panic_information: &::core::panic::PanicInfo) -> ! { 98 | eprintln!("\nKernel {}", panic_information); 99 | ::debug::stack_trace(); 100 | loop { unsafe { asm!("hlt") } }; 101 | } 102 | 103 | #[lang = "oom"] 104 | #[cfg(not(test))] 105 | #[no_mangle] 106 | pub extern fn oom() -> ! { 107 | panic!("Out of memory"); 108 | } 109 | -------------------------------------------------------------------------------- /kernel/src/memory/address.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct PhysicalAddress(u64); 3 | 4 | impl PhysicalAddress { 5 | pub fn new(raw: u64) -> PhysicalAddress { 6 | assert!(raw < ::KERNEL_BASE, "Physical address {:#x} is greater than ::KERNEL_BASE", raw); 7 | PhysicalAddress::new_unchecked(raw) 8 | } 9 | 10 | /// Creates a PhysicalAddress from a higher half address 11 | pub fn new_force_adjust(raw: u64) -> PhysicalAddress { 12 | assert!(raw >= ::KERNEL_BASE, "Physical address {:#x} is not a higher half address", raw); 13 | PhysicalAddress::new_unchecked(raw - ::KERNEL_BASE as u64) 14 | } 15 | 16 | /// Creates a PhysicalAddress and adjusts it if it is a higher half address 17 | pub fn new_adjusted(raw: u64) -> PhysicalAddress { 18 | if raw >= ::KERNEL_BASE { 19 | return PhysicalAddress::new_force_adjust(raw); 20 | } 21 | PhysicalAddress::new(raw) 22 | } 23 | 24 | pub fn new_unchecked(raw: u64) -> PhysicalAddress { 25 | PhysicalAddress(raw) 26 | } 27 | 28 | pub fn raw(&self) -> u64 { 29 | self.0 30 | } 31 | 32 | pub fn align_up(&self, multiple: u64) -> PhysicalAddress { 33 | PhysicalAddress::new(::utility::math::align_up_u64(self.raw(), multiple)) 34 | } 35 | 36 | pub fn align_down(&self, multiple: u64) -> PhysicalAddress { 37 | PhysicalAddress::new(::utility::math::align_down_u64(self.raw(), multiple)) 38 | } 39 | } 40 | 41 | impl PartialEq for PhysicalAddress { 42 | fn eq(&self, other: &Self) -> bool { 43 | self.raw().eq(&other.raw()) 44 | } 45 | } 46 | 47 | impl PartialOrd for PhysicalAddress { 48 | fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> { 49 | Some(self.raw().cmp(&other.raw())) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /kernel/src/memory/frame.rs: -------------------------------------------------------------------------------- 1 | use paging::PageLike; 2 | use super::MemoryArea; 3 | use super::PhysicalAddress; 4 | 5 | pub trait FrameLike { 6 | type PageType: PageLike; 7 | 8 | fn size() -> u64 where Self: Sized; 9 | fn from_index(index: usize) -> Self where Self: Sized; 10 | fn index(&self) -> usize; 11 | 12 | fn from_address(address: PhysicalAddress) -> Self where Self: Sized { 13 | Self::from_index((address.raw() / Self::size()) as usize) 14 | } 15 | 16 | fn start_address(&self) -> PhysicalAddress where Self: Sized { 17 | PhysicalAddress::new(self.index() as u64 * Self::size()) 18 | } 19 | 20 | fn end_address(&self) -> PhysicalAddress where Self: Sized { 21 | PhysicalAddress::new(self.start_address().raw() + (Self::size() - 1)) 22 | } 23 | 24 | fn to_memory_area(&self) -> MemoryArea where Self: Sized { 25 | MemoryArea::new(self.start_address(), (Self::size() - 1) as usize) 26 | } 27 | } 28 | 29 | #[derive(Debug, Clone)] 30 | pub struct FrameIter { 31 | next: usize, 32 | end: usize, 33 | _frame_type: ::core::marker::PhantomData, 34 | } 35 | 36 | impl FrameIter { 37 | pub fn inclusive(start: F, end: F) -> FrameIter { 38 | assert!(start.index() <= end.index()); 39 | Self::inclusive_unchecked(start, end) 40 | } 41 | 42 | pub fn inclusive_unchecked(start: F, end: F) -> FrameIter { 43 | FrameIter { 44 | next: start.index(), 45 | end: end.index(), 46 | _frame_type: Default::default(), 47 | } 48 | } 49 | 50 | pub fn previous_next(&self) -> F { 51 | F::from_index(self.next - 1) 52 | } 53 | 54 | pub fn skip_to(&mut self, next: F) { 55 | self.next = next.index(); 56 | } 57 | } 58 | 59 | impl Iterator for FrameIter { 60 | type Item = F; 61 | 62 | fn next(&mut self) -> Option<::Item> { 63 | if self.next <= self.end { 64 | self.next += 1; 65 | Some(F::from_index(self.next - 1)) 66 | } else { 67 | None 68 | } 69 | } 70 | } 71 | 72 | #[derive(Debug, Clone)] 73 | pub struct Frame { 74 | index: usize, 75 | } 76 | 77 | impl Frame { 78 | pub const SIZE: u64 = ::paging::Page::SIZE; 79 | } 80 | 81 | impl FrameLike for Frame { 82 | type PageType = ::paging::Page; 83 | 84 | fn size() -> u64 { 85 | Frame::SIZE 86 | } 87 | fn from_index(index: usize) -> Frame { 88 | Frame { 89 | index, 90 | } 91 | } 92 | fn index(&self) -> usize { 93 | self.index 94 | } 95 | } 96 | 97 | impl PartialEq for Frame { 98 | fn eq(&self, other: &Self) -> bool { 99 | self.index().eq(&other.index()) 100 | } 101 | } 102 | 103 | impl PartialOrd for Frame { 104 | fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> { 105 | self.index().partial_cmp(&other.index()) 106 | } 107 | } 108 | 109 | #[derive(Debug, Clone)] 110 | pub struct HugeFrame { 111 | index: usize, 112 | } 113 | 114 | impl HugeFrame { 115 | pub const SIZE: u64 = ::paging::HugePage::SIZE; 116 | } 117 | 118 | impl FrameLike for HugeFrame { 119 | type PageType = ::paging::HugePage; 120 | 121 | fn size() -> u64 { 122 | HugeFrame::SIZE 123 | } 124 | 125 | fn from_index(index: usize) -> HugeFrame { 126 | HugeFrame { 127 | index, 128 | } 129 | } 130 | 131 | fn index(&self) -> usize { 132 | self.index 133 | } 134 | } 135 | 136 | impl PartialEq for HugeFrame { 137 | fn eq(&self, other: &Self) -> bool { 138 | self.index().eq(&other.index()) 139 | } 140 | } 141 | 142 | impl PartialOrd for HugeFrame { 143 | fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> { 144 | self.index().partial_cmp(&other.index()) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /kernel/src/memory/frame_allocator.rs: -------------------------------------------------------------------------------- 1 | use super::FrameLike; 2 | 3 | pub trait FrameLikeAllocator { 4 | fn free_frames_count(&self) -> usize; 5 | fn used_frames_count(&self) -> usize; 6 | 7 | fn allocate(&mut self) -> Option; 8 | fn deallocate(&mut self, frame: F); 9 | } 10 | -------------------------------------------------------------------------------- /kernel/src/memory/frame_allocators/huge_frame_divider.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::FrameIter; 3 | use memory::FrameLike; 4 | use memory::HugeFrame; 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct HugeFrameDivider { 8 | frames: FrameIter, 9 | allocated_count: usize, 10 | } 11 | 12 | impl HugeFrameDivider { 13 | pub const FRAME_COUNT: usize = (HugeFrame::SIZE / Frame::SIZE) as usize; 14 | 15 | pub fn new(huge_frame: HugeFrame) -> HugeFrameDivider { 16 | let start_frame = Frame::from_address(huge_frame.start_address()); 17 | let end_frame = Frame::from_address(huge_frame.end_address()); 18 | HugeFrameDivider { 19 | frames: FrameIter::inclusive(start_frame, end_frame), 20 | allocated_count: 0, 21 | } 22 | } 23 | } 24 | 25 | impl super::FrameLikeAllocator for HugeFrameDivider { 26 | fn free_frames_count(&self) -> usize { 27 | Self::FRAME_COUNT - self.used_frames_count() 28 | } 29 | 30 | fn used_frames_count(&self) -> usize { 31 | self.allocated_count 32 | } 33 | 34 | fn allocate(&mut self) -> Option { 35 | let frame = self.frames.next(); 36 | if frame.is_some() { 37 | self.allocated_count += 1; 38 | } 39 | frame 40 | } 41 | 42 | fn deallocate(&mut self, _frame: Frame) { 43 | panic!("HugeFrameDivider does not support deallocation of frames") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kernel/src/memory/frame_allocators/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::huge_frame_divider::HugeFrameDivider; 2 | pub use self::tiny_allocator::TinyAllocator; 3 | use super::FrameLikeAllocator; 4 | 5 | pub mod huge_frame_divider; 6 | pub mod tiny_allocator; -------------------------------------------------------------------------------- /kernel/src/memory/frame_allocators/tiny_allocator.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use super::FrameLikeAllocator; 3 | 4 | macro_rules! frame_count { () => { 3 }; } 5 | 6 | type FramePool = [Option; frame_count!()]; 7 | 8 | pub struct TinyAllocator { 9 | pool: FramePool, 10 | } 11 | 12 | impl TinyAllocator { 13 | pub fn new(allocator: &mut FrameLikeAllocator) -> TinyAllocator { 14 | let mut f = || allocator.allocate(); 15 | let pool = [f(), f(), f()]; 16 | TinyAllocator { 17 | pool, 18 | } 19 | } 20 | 21 | pub fn fill(&mut self, allocator: &mut FrameLikeAllocator) { 22 | for frame in self.pool.iter_mut() { 23 | if frame.is_none() { 24 | *frame = Some(allocator.allocate().expect("Out of memory: TinyAllocator")); 25 | } 26 | } 27 | } 28 | 29 | pub fn dispose(&mut self, allocator: &mut FrameLikeAllocator) { 30 | for frame in self.pool.iter_mut() { 31 | if let Some(frame) = frame.take() { 32 | allocator.deallocate(frame); 33 | } 34 | } 35 | } 36 | } 37 | 38 | impl FrameLikeAllocator for TinyAllocator { 39 | fn free_frames_count(&self) -> usize { 40 | self.pool.iter().filter(|f| f.is_some()).count() 41 | } 42 | 43 | fn used_frames_count(&self) -> usize { 44 | self.pool.iter().filter(|f| f.is_none()).count() 45 | } 46 | 47 | fn allocate(&mut self) -> Option { 48 | self.pool.iter_mut() 49 | .find(|f| f.is_some()) 50 | .and_then(|f| f.take()) 51 | } 52 | 53 | fn deallocate(&mut self, frame: Frame) { 54 | *self.pool.iter_mut() 55 | .find(|f| f.is_none()) 56 | .expect(concat!("Tiny allocator can only hold ", frame_count!(), " frames")) 57 | = Some(frame); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kernel/src/memory/functions.rs: -------------------------------------------------------------------------------- 1 | use core::ops::DerefMut; 2 | use linked_list_allocator::LockedHeap; 3 | use memory::FrameLikeAllocator; 4 | use paging::EntryFlags; 5 | use paging::PageLike; 6 | use paging::reserved::HEAP_BOTTOM; 7 | use paging::VirtualAddress; 8 | use super::generic_allocators::BootAllocator; 9 | use super::generic_allocators::GlobalFrameAllocator; 10 | use utility::Global; 11 | use x86_64::structures::idt::PageFaultErrorCode; 12 | 13 | #[cfg_attr(not(test), global_allocator)] 14 | pub static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 15 | pub static FRAME_ALLOCATOR: Global = Global::new("FRAME_ALLOCATOR"); 16 | 17 | pub fn initialize(boot_structure: ::utility::MultibootStructure) -> BootAllocator { 18 | let _status = ::display::text_mode::BootStatus::new("Creating boot frame allocator"); 19 | BootAllocator::new(boot_structure) 20 | } 21 | 22 | pub fn post_paging_initialize(mut allocator: BootAllocator) { 23 | let _status = ::display::text_mode::BootStatus::new("Initializing kernel heap"); 24 | create_initial_heap(&mut allocator); 25 | unsafe { HEAP_ALLOCATOR.lock().init(HEAP_BOTTOM.raw(), ::paging::reserved::HEAP_SIZE); } 26 | FRAME_ALLOCATOR.set(GlobalFrameAllocator::Boot(allocator)); 27 | } 28 | 29 | pub fn post_initialize(boot_information: &::multiboot2::BootInformation) { 30 | let _status = ::display::text_mode::BootStatus::new("Creating system frame allocator"); 31 | 32 | // We must be careful to not do any heap allocation when 33 | // converting the BootAllocator. Heap allocation requires 34 | // access to the BootAllocator but to convert it, we have 35 | // to lock it. This means if we heap allocate, it will 36 | // cause a deadlock. See structures/frame_store 37 | let mut free_areas = ::alloc::Vec::new(); 38 | for area in boot_information.memory_map_tag().unwrap().memory_areas() { 39 | let memory_area: super::MemoryArea = area.into(); 40 | free_areas.push(memory_area); 41 | } 42 | 43 | FRAME_ALLOCATOR.lock().convert(free_areas); 44 | } 45 | 46 | fn create_initial_heap(allocator: &mut A) where A: ::memory::GenericAllocator { 47 | let mut table = ::paging::ACTIVE_PAGE_TABLE.lock(); 48 | let page = ::paging::HugePage::from_address(HEAP_BOTTOM.clone()); 49 | let frame = allocator.allocate().expect("Out of memory: Initial heap"); 50 | table.map_to(page, frame, EntryFlags::WRITABLE, allocator); 51 | } 52 | 53 | pub fn handle_heap_fault(address: VirtualAddress, _error_code: &PageFaultErrorCode) -> bool { 54 | // The kernel's heap does not have a predefined size; 55 | // instead, when an address is in the heap but not 56 | // allocated, we allocate it here. This allows the 57 | // heap to grow as large as needed 58 | 59 | if !(address >= HEAP_BOTTOM && address <= ::paging::reserved::HEAP_TOP) { 60 | return false; 61 | } 62 | 63 | let page = ::paging::HugePage::from_address(address); 64 | 65 | // Note: We lock the FRAME_ALLOCATOR here which results 66 | // in a deadlock if it was already locked 67 | let mut allocator = FRAME_ALLOCATOR.lock(); 68 | let frame = allocator.allocate().expect("Out of memory: Heap fault"); 69 | 70 | // Same goes for the ACTIVE_PAGE_TABLE. 71 | let mut table = ::paging::ACTIVE_PAGE_TABLE.lock(); 72 | table.map_to(page, frame, EntryFlags::WRITABLE, allocator.deref_mut()); 73 | true 74 | } -------------------------------------------------------------------------------- /kernel/src/memory/generic_allocators/boot_allocator.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::frame_allocators::HugeFrameDivider; 3 | use memory::huge_frame_allocators::huge_boot_bump_allocator::*; 4 | use memory::HugeFrame; 5 | use paging::Page; 6 | use paging::PageLike; 7 | use structures::FrameStore; 8 | use super::FixedFrameRecycler; 9 | use super::FrameLikeAllocator; 10 | use super::FrameRecycler; 11 | use super::PostBootAllocator; 12 | 13 | pub struct BootAllocator { 14 | frame_allocator: Option>, 15 | huge_frame_allocator: HugeBootBumpAllocator, 16 | } 17 | 18 | // For information on the design of these allocators, 19 | // see the mod.rs in memory/generic_allocators 20 | impl BootAllocator { 21 | pub fn new(boot_structure: ::utility::MultibootStructure) -> BootAllocator { 22 | let mut huge_frame_allocator = HugeBootBumpAllocator::new(boot_structure); 23 | let initial_huge_frame = huge_frame_allocator.allocate() 24 | .expect("Not enough memory"); 25 | let frame_allocator = FixedFrameRecycler::new(HugeFrameDivider::new(initial_huge_frame)); 26 | Self { 27 | frame_allocator: Some(frame_allocator), 28 | huge_frame_allocator, 29 | } 30 | } 31 | 32 | pub fn convert(&mut self, free_areas: ::alloc::Vec<::memory::MemoryArea>) -> PostBootAllocator { 33 | use memory::huge_frame_allocators::HugeBumpAllocator; 34 | let mut allocator_frame_store = self.allocator_frame_store(); 35 | let huge_allocator_frame_store = self.huge_allocator_frame_store(); 36 | 37 | let (mut free_frames, frame_allocator) = self.frame_allocator.take().unwrap().unwrap(); 38 | for free_frame in &mut free_frames { 39 | if free_frame.is_some() { 40 | allocator_frame_store.push(free_frame.take().unwrap(), self); 41 | } 42 | } 43 | 44 | let frame_recycler = FrameRecycler::new_custom(frame_allocator, allocator_frame_store); 45 | 46 | let kernel_area = self.huge_frame_allocator.get_kernel_area(); 47 | let next_frame = self.huge_frame_allocator.allocate().expect("Out of memory: Allocator conversion"); 48 | let huge_bump_allocator = HugeBumpAllocator::new_custom(free_areas, kernel_area, next_frame); 49 | let huge_frame_recycler = FrameRecycler::new_custom(huge_bump_allocator, huge_allocator_frame_store); 50 | PostBootAllocator::new(frame_recycler, huge_frame_recycler) 51 | } 52 | 53 | fn allocator_frame_store(&mut self) -> FrameStore { 54 | let start = Page::from_address(::paging::reserved::FRAME_STORE_BOTTOM); 55 | let end = Page::from_address(::paging::reserved::FRAME_STORE_TOP); 56 | let pages = ::paging::PageIter::inclusive(start, end); 57 | FrameStore::new(pages, self) 58 | } 59 | 60 | fn huge_allocator_frame_store(&mut self) -> FrameStore { 61 | use memory::FrameLike; 62 | 63 | let mut frame_store = { 64 | let start = Page::from_address(::paging::reserved::HUGE_FRAME_STORE_BOTTOM); 65 | let end = Page::from_address(::paging::reserved::HUGE_FRAME_STORE_TOP); 66 | let pages = ::paging::PageIter::inclusive(start, end); 67 | FrameStore::new(pages, self) 68 | }; 69 | 70 | for used_area in self.huge_frame_allocator.used_areas.clone().into_iter().skip(1) { 71 | for free_area in self.huge_frame_allocator.free_areas.clone() { 72 | let free_area = ::memory::MemoryArea::from(free_area); 73 | if used_area.overlap(&free_area).is_some() { 74 | let start = HugeFrame::from_address(used_area.start_address()); 75 | let end = HugeFrame::from_address(used_area.end_address()); 76 | for frame in ::memory::FrameIter::inclusive(start, end) { 77 | frame_store.push(frame, self); 78 | } 79 | } 80 | } 81 | } 82 | 83 | frame_store 84 | } 85 | 86 | fn frame_allocator(&self) -> &FixedFrameRecycler { 87 | self.frame_allocator.as_ref().unwrap() 88 | } 89 | 90 | fn frame_allocator_mut(&mut self) -> &mut FixedFrameRecycler { 91 | self.frame_allocator.as_mut().unwrap() 92 | } 93 | } 94 | 95 | impl ::memory::FrameLikeAllocator for BootAllocator { 96 | fn free_frames_count(&self) -> usize { 97 | (self.huge_frame_allocator.free_frames_count() * HugeFrameDivider::FRAME_COUNT) + self.frame_allocator().free_frames_count() 98 | } 99 | 100 | fn used_frames_count(&self) -> usize { 101 | (self.huge_frame_allocator.used_frames_count() * HugeFrameDivider::FRAME_COUNT) - self.frame_allocator().free_frames_count() 102 | } 103 | 104 | fn allocate(&mut self) -> Option { 105 | let frame = self.frame_allocator_mut().allocate(); 106 | if frame.is_some() { 107 | return frame; 108 | } 109 | 110 | let huge_page = self.huge_frame_allocator.allocate()?; 111 | self.frame_allocator_mut().set(HugeFrameDivider::new(huge_page)); 112 | self.frame_allocator_mut().allocate() 113 | } 114 | 115 | fn deallocate(&mut self, frame: Frame) { 116 | self.frame_allocator_mut().deallocate(frame); 117 | } 118 | } 119 | 120 | impl ::memory::FrameLikeAllocator for BootAllocator { 121 | fn free_frames_count(&self) -> usize { 122 | self.huge_frame_allocator.free_frames_count() 123 | } 124 | 125 | fn used_frames_count(&self) -> usize { 126 | self.huge_frame_allocator.used_frames_count() 127 | } 128 | 129 | fn allocate(&mut self) -> Option { 130 | self.huge_frame_allocator.allocate() 131 | } 132 | 133 | fn deallocate(&mut self, _frame: HugeFrame) { 134 | panic!("BootAllocator does not support deallocation of huge frames") 135 | } 136 | } 137 | 138 | impl super::GenericAllocator for BootAllocator {} 139 | -------------------------------------------------------------------------------- /kernel/src/memory/generic_allocators/fixed_frame_recycler.rs: -------------------------------------------------------------------------------- 1 | use memory::FrameLike; 2 | use super::FrameLikeAllocator; 3 | 4 | // The amount of frames that can be deallocated 5 | macro_rules! amount { 6 | () => { 16 }; 7 | } 8 | 9 | pub const MAX_FRAMES: usize = amount!(); 10 | 11 | type Frames = [Option; MAX_FRAMES]; 12 | 13 | // This is an example of the Decorator pattern 14 | pub struct FixedFrameRecycler where F: FrameLike, A: FrameLikeAllocator { 15 | allocator: A, 16 | free_frames: Frames, 17 | used_frames: usize, 18 | } 19 | 20 | impl FixedFrameRecycler where F: FrameLike, A: FrameLikeAllocator { 21 | pub fn new(allocator: A) -> FixedFrameRecycler { 22 | Self { 23 | allocator, 24 | free_frames: Default::default(), 25 | used_frames: 0, 26 | } 27 | } 28 | 29 | pub fn set(&mut self, allocator: A) { 30 | self.allocator = allocator; 31 | } 32 | 33 | pub fn unwrap(self) -> (Frames, A) { 34 | (self.free_frames, self.allocator) 35 | } 36 | 37 | fn free_frames(&self) -> usize { 38 | MAX_FRAMES - self.used_frames 39 | } 40 | } 41 | 42 | impl FrameLikeAllocator for FixedFrameRecycler 43 | where F: FrameLike, A: FrameLikeAllocator { 44 | fn free_frames_count(&self) -> usize { 45 | self.allocator.free_frames_count() + self.free_frames() 46 | } 47 | 48 | fn used_frames_count(&self) -> usize { 49 | self.allocator.used_frames_count() - self.free_frames() 50 | } 51 | 52 | fn allocate(&mut self) -> Option { 53 | self.free_frames.iter_mut() 54 | .find(|f| f.is_some()) 55 | .and_then(|f| f.take()) 56 | .or_else(|| self.allocator.allocate()) 57 | } 58 | 59 | fn deallocate(&mut self, frame: F) { 60 | *self.free_frames.iter_mut() 61 | .find(|f| f.is_none()) 62 | .expect(concat!("FixedFrameRecycler can only hold ", amount!(), " free frames")) = Some(frame); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kernel/src/memory/generic_allocators/frame_recycler.rs: -------------------------------------------------------------------------------- 1 | use memory::FrameLike; 2 | use structures::FrameStore; 3 | use super::FrameLikeAllocator; 4 | 5 | pub struct FrameRecycler where F: FrameLike, A: FrameLikeAllocator { 6 | allocator: A, 7 | free_frames: FrameStore, 8 | } 9 | 10 | impl FrameRecycler where F: FrameLike, A: FrameLikeAllocator { 11 | pub fn new_custom(allocator: A, free_frames: FrameStore) -> FrameRecycler { 12 | FrameRecycler { 13 | allocator, 14 | free_frames, 15 | } 16 | } 17 | 18 | pub fn set(&mut self, allocator: A) { 19 | self.allocator = allocator; 20 | } 21 | 22 | pub fn deallocate(&mut self, allocator: &mut FrameLikeAllocator<::memory::Frame>, frame: F) { 23 | self.free_frames.push(frame, allocator); 24 | } 25 | } 26 | 27 | impl FrameLikeAllocator for FrameRecycler 28 | where F: FrameLike, A: FrameLikeAllocator { 29 | fn free_frames_count(&self) -> usize { 30 | self.allocator.free_frames_count() + self.free_frames.size() 31 | } 32 | 33 | fn used_frames_count(&self) -> usize { 34 | self.allocator.used_frames_count() - self.free_frames.size() 35 | } 36 | 37 | fn allocate(&mut self) -> Option { 38 | self.free_frames.pop().or_else(|| self.allocator.allocate()) 39 | } 40 | 41 | fn deallocate(&mut self, _frame: F) { 42 | panic!("Deallocation requires a reference to a frame allocator") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kernel/src/memory/generic_allocators/global_frame_allocator.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::HugeFrame; 3 | use super::BootAllocator; 4 | use super::FrameLikeAllocator; 5 | use super::GenericAllocator; 6 | use super::PostBootAllocator; 7 | 8 | pub enum GlobalFrameAllocator { 9 | Boot(BootAllocator), 10 | PostBoot(PostBootAllocator), 11 | } 12 | 13 | impl GlobalFrameAllocator { 14 | fn allocator(&self) -> &GenericAllocator { 15 | use self::GlobalFrameAllocator::*; 16 | match self { 17 | Boot(allocator) => allocator, 18 | PostBoot(allocator) => allocator, 19 | } 20 | } 21 | 22 | fn allocator_mut(&mut self) -> &mut GenericAllocator { 23 | use self::GlobalFrameAllocator::*; 24 | match self { 25 | Boot(allocator) => allocator, 26 | PostBoot(allocator) => allocator, 27 | } 28 | } 29 | 30 | pub fn convert(&mut self, free_areas: ::alloc::Vec<::memory::MemoryArea>) { 31 | use core::mem::replace; 32 | use self::GlobalFrameAllocator::*; 33 | let conversion; 34 | if let Boot(ref mut allocator) = self { 35 | conversion = GlobalFrameAllocator::PostBoot(allocator.convert(free_areas)); 36 | } else { 37 | panic!("Cannot convert current allocator variant"); 38 | } 39 | replace(self, conversion); 40 | } 41 | } 42 | 43 | impl ::memory::FrameLikeAllocator for GlobalFrameAllocator { 44 | fn free_frames_count(&self) -> usize { 45 | FrameLikeAllocator::::free_frames_count(self.allocator()) 46 | } 47 | 48 | fn used_frames_count(&self) -> usize { 49 | FrameLikeAllocator::::used_frames_count(self.allocator()) 50 | } 51 | 52 | fn allocate(&mut self) -> Option { 53 | FrameLikeAllocator::::allocate(self.allocator_mut()) 54 | } 55 | 56 | fn deallocate(&mut self, frame: Frame) { 57 | FrameLikeAllocator::::deallocate(self.allocator_mut(), frame); 58 | } 59 | } 60 | 61 | impl ::memory::FrameLikeAllocator for GlobalFrameAllocator { 62 | fn free_frames_count(&self) -> usize { 63 | FrameLikeAllocator::::free_frames_count(self.allocator()) 64 | } 65 | 66 | fn used_frames_count(&self) -> usize { 67 | FrameLikeAllocator::::used_frames_count(self.allocator()) 68 | } 69 | 70 | fn allocate(&mut self) -> Option { 71 | FrameLikeAllocator::::allocate(self.allocator_mut()) 72 | } 73 | 74 | fn deallocate(&mut self, frame: HugeFrame) { 75 | FrameLikeAllocator::::deallocate(self.allocator_mut(), frame); 76 | } 77 | } 78 | 79 | impl super::GenericAllocator for GlobalFrameAllocator {} 80 | -------------------------------------------------------------------------------- /kernel/src/memory/generic_allocators/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::boot_allocator::BootAllocator; 2 | pub use self::fixed_frame_recycler::FixedFrameRecycler; 3 | pub use self::frame_recycler::FrameRecycler; 4 | pub use self::global_frame_allocator::GlobalFrameAllocator; 5 | pub use self::post_boot_allocator::PostBootAllocator; 6 | use super::FrameLikeAllocator; 7 | 8 | pub mod boot_allocator; 9 | pub mod global_frame_allocator; 10 | pub mod frame_recycler; 11 | pub mod fixed_frame_recycler; 12 | pub mod post_boot_allocator; 13 | 14 | // The BootAllocator and PostBootAllocator work in two stages: 15 | // If there are no frames available, a huge frame is allocated 16 | // and then split into 512 frames. This allows for easy allocation 17 | // of both huge frames and regular frames. 18 | 19 | pub trait GenericAllocator: FrameLikeAllocator + FrameLikeAllocator {} 20 | -------------------------------------------------------------------------------- /kernel/src/memory/generic_allocators/post_boot_allocator.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::frame_allocators::HugeFrameDivider; 3 | use memory::frame_allocators::TinyAllocator; 4 | use memory::huge_frame_allocators::HugeBumpAllocator; 5 | use memory::HugeFrame; 6 | use super::FrameLikeAllocator; 7 | use super::FrameRecycler; 8 | 9 | pub struct PostBootAllocator { 10 | frame_allocator: FrameRecycler, 11 | huge_frame_allocator: FrameRecycler, 12 | pool: Option, 13 | } 14 | 15 | impl PostBootAllocator { 16 | pub fn new(mut frame_allocator: FrameRecycler, 17 | huge_frame_allocator: FrameRecycler) -> PostBootAllocator { 18 | let pool = TinyAllocator::new(&mut frame_allocator); 19 | Self { 20 | frame_allocator, 21 | huge_frame_allocator, 22 | pool: Some(pool), 23 | } 24 | } 25 | 26 | fn take_pool(&mut self) -> TinyAllocator { 27 | let mut pool = self.pool.take().unwrap(); 28 | pool.fill(self); 29 | pool 30 | } 31 | } 32 | 33 | impl FrameLikeAllocator for PostBootAllocator { 34 | fn free_frames_count(&self) -> usize { 35 | (self.huge_frame_allocator.free_frames_count() * HugeFrameDivider::FRAME_COUNT) 36 | + self.frame_allocator.free_frames_count() 37 | } 38 | 39 | fn used_frames_count(&self) -> usize { 40 | (self.huge_frame_allocator.used_frames_count() * HugeFrameDivider::FRAME_COUNT) 41 | - self.frame_allocator.free_frames_count() 42 | } 43 | 44 | fn allocate(&mut self) -> Option { 45 | let frame = self.frame_allocator.allocate(); 46 | if frame.is_some() { 47 | return frame; 48 | } 49 | 50 | let huge_page = self.huge_frame_allocator.allocate()?; 51 | self.frame_allocator.set(HugeFrameDivider::new(huge_page)); 52 | self.frame_allocator.allocate() 53 | } 54 | 55 | fn deallocate(&mut self, frame: Frame) { 56 | let mut pool = self.take_pool(); 57 | self.frame_allocator.deallocate(&mut pool, frame); 58 | self.pool = Some(pool); 59 | } 60 | } 61 | 62 | impl FrameLikeAllocator for PostBootAllocator { 63 | fn free_frames_count(&self) -> usize { 64 | self.huge_frame_allocator.free_frames_count() 65 | } 66 | 67 | fn used_frames_count(&self) -> usize { 68 | self.huge_frame_allocator.used_frames_count() 69 | } 70 | 71 | fn allocate(&mut self) -> Option { 72 | self.huge_frame_allocator.allocate() 73 | } 74 | 75 | fn deallocate(&mut self, frame: HugeFrame) { 76 | let mut pool = self.take_pool(); 77 | self.huge_frame_allocator.deallocate(&mut pool, frame); 78 | self.pool = Some(pool); 79 | } 80 | } 81 | 82 | impl super::GenericAllocator for PostBootAllocator {} 83 | -------------------------------------------------------------------------------- /kernel/src/memory/huge_frame_allocators/huge_boot_bump_allocator.rs: -------------------------------------------------------------------------------- 1 | use memory::FrameIter; 2 | use memory::FrameLike; 3 | use memory::HugeFrame; 4 | use memory::MemoryArea; 5 | use memory::PhysicalAddress; 6 | use super::FrameLikeAllocator; 7 | use utility::MultibootStructure; 8 | 9 | type UsedAreas = [MemoryArea; 2]; 10 | 11 | pub struct HugeBootBumpAllocator { 12 | boot_structure: MultibootStructure, 13 | next_huge_frame: FrameIter, 14 | 15 | pub used_areas: UsedAreas, 16 | pub free_areas: ::multiboot2::MemoryAreaIter, 17 | 18 | total_frames: usize, 19 | used_frames: usize, 20 | } 21 | 22 | impl HugeBootBumpAllocator { 23 | pub fn new(boot_structure: MultibootStructure) -> HugeBootBumpAllocator { 24 | let boot_information = boot_structure.get(); 25 | let free_areas = boot_information.memory_map_tag() 26 | .expect("MemoryMapTag not available") 27 | .memory_areas(); 28 | let used_areas = Self::create_used_areas(&boot_information); 29 | let (total_frames, used_frames) = Self::calculate_frames(&boot_information, &used_areas); 30 | let dummy_frame = HugeFrame::from_index(0); 31 | let mut allocator = HugeBootBumpAllocator { 32 | boot_structure, 33 | next_huge_frame: FrameIter::inclusive(dummy_frame.clone(), dummy_frame), 34 | used_areas, 35 | free_areas, 36 | total_frames, 37 | used_frames, 38 | }; 39 | allocator.next_huge_frame = allocator.next_free_area().expect("Not enough memory"); 40 | allocator 41 | } 42 | 43 | pub fn get_kernel_area(&self) -> MemoryArea { 44 | Self::create_kernel_area(&self.boot_structure.get().elf_sections_tag().unwrap()) 45 | } 46 | 47 | fn create_kernel_area(elf_sections: &::multiboot2::ElfSectionsTag) -> MemoryArea { 48 | let kernel_start = elf_sections.sections().map(|s| s.start_address()).min().unwrap(); 49 | let kernel_end = elf_sections.sections().map(|s| s.end_address()).max().unwrap() - ::KERNEL_BASE as u64; 50 | MemoryArea::new(PhysicalAddress::new(kernel_start), (kernel_end - kernel_start) as usize) 51 | } 52 | 53 | fn create_used_areas(boot_information: &::multiboot2::BootInformation) -> UsedAreas { 54 | let elf_sections = boot_information.elf_sections_tag().expect("ElfSectionsTag not available"); 55 | 56 | let multiboot_size = boot_information.total_size(); 57 | let multiboot_start = boot_information.start_address() as u64; 58 | 59 | [ 60 | Self::create_kernel_area(&elf_sections), 61 | MemoryArea::new(PhysicalAddress::new_force_adjust(multiboot_start), multiboot_size), 62 | ] 63 | } 64 | 65 | fn next_free_area(&mut self) -> Option> { 66 | let next_free_area: MemoryArea = self.free_areas.next()?.into(); 67 | 68 | let start = next_free_area.start_address().align_up(HugeFrame::SIZE); 69 | let end = next_free_area.end_address().align_down(HugeFrame::SIZE).raw(); 70 | let end = PhysicalAddress::new(end.saturating_sub(1)); 71 | if (end.raw() - start.raw()) < HugeFrame::SIZE { 72 | return self.next_free_area(); 73 | } 74 | 75 | Some(FrameIter::inclusive(HugeFrame::from_address(start), 76 | HugeFrame::from_address(end))) 77 | } 78 | 79 | /// Returns true if the frame does not overlap any used areas 80 | fn validate_frame(&mut self, frame: &HugeFrame) -> bool { 81 | let validate_area = |used_area: &MemoryArea| -> Option { 82 | if used_area.overlap(&frame.to_memory_area()).is_some() { 83 | let next_valid_frame_address = used_area.end_address().align_up(HugeFrame::SIZE); 84 | return Some(HugeFrame::from_address(next_valid_frame_address)); 85 | } 86 | None 87 | }; 88 | 89 | for used_area in &self.used_areas { 90 | if let Some(next_valid_frame) = validate_area(used_area) { 91 | self.next_huge_frame.skip_to(next_valid_frame); 92 | return false; 93 | } 94 | } 95 | 96 | for module_area in self.module_areas() { 97 | if let Some(next_valid_frame) = validate_area(&module_area.into()) { 98 | self.next_huge_frame.skip_to(next_valid_frame); 99 | return false; 100 | } 101 | } 102 | 103 | true 104 | } 105 | 106 | /// Returns the initial total and used frames 107 | fn calculate_frames(boot_information: &::multiboot2::BootInformation, used_areas: &UsedAreas) -> (usize, usize) { 108 | let free_areas = boot_information.memory_map_tag().unwrap().memory_areas(); 109 | let mut total_frames = 0; 110 | let mut used_frames = 0; 111 | 112 | { 113 | let mut add_overlap = |used_area: &MemoryArea| { 114 | let start = HugeFrame::from_address(used_area.start_address()); 115 | let end = HugeFrame::from_address(used_area.end_address()); 116 | used_frames += end.index() - start.index() + 1; 117 | }; 118 | 119 | for used_area in used_areas.iter() { 120 | add_overlap(used_area); 121 | } 122 | 123 | for module in boot_information.module_tags() { 124 | add_overlap(&module.into()) 125 | } 126 | } 127 | 128 | for free_area in free_areas { 129 | let free_area: MemoryArea = free_area.into(); 130 | let start = free_area.start_address().align_up(HugeFrame::SIZE); 131 | let end = free_area.end_address().align_down(HugeFrame::SIZE).raw(); 132 | let end = PhysicalAddress::new(end.saturating_sub(1)); 133 | total_frames += (HugeFrame::from_address(end).index() 134 | - HugeFrame::from_address(start).index()) + 1; 135 | } 136 | (total_frames, used_frames) 137 | } 138 | 139 | fn module_areas(&self) -> ::multiboot2::ModuleIter { 140 | self.boot_structure.get().module_tags() 141 | } 142 | } 143 | 144 | impl FrameLikeAllocator for HugeBootBumpAllocator { 145 | /// Returns an estimate of the amount of frames left for allocation 146 | /// 147 | /// Count is understated due to two or more distinct used areas 148 | /// being in the same huge frame 149 | /// 150 | fn free_frames_count(&self) -> usize { 151 | self.total_frames.saturating_sub(self.used_frames_count()) 152 | } 153 | 154 | fn used_frames_count(&self) -> usize { 155 | self.used_frames 156 | } 157 | 158 | fn allocate(&mut self) -> Option { 159 | let frame = self.next_huge_frame.next(); 160 | match frame { 161 | Some(frame) => { 162 | if self.validate_frame(&frame) { 163 | self.used_frames += 1; 164 | return Some(frame); 165 | } 166 | } 167 | None => { 168 | self.next_huge_frame = self.next_free_area()?; 169 | } 170 | } 171 | self.allocate() 172 | } 173 | 174 | fn deallocate(&mut self, _frame: HugeFrame) { 175 | panic!("HugeBootBumpAllocator does not support deallocation of huge frames") 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /kernel/src/memory/huge_frame_allocators/huge_bump_allocator.rs: -------------------------------------------------------------------------------- 1 | use alloc::Vec; 2 | use memory::FrameIter; 3 | use memory::FrameLike; 4 | use memory::HugeFrame; 5 | use memory::MemoryArea; 6 | use memory::PhysicalAddress; 7 | 8 | pub struct HugeBumpAllocator { 9 | free_areas: Vec, 10 | next_free_area: usize, 11 | 12 | next_huge_frame: FrameIter, 13 | kernel: MemoryArea, 14 | 15 | total_frames: usize, 16 | used_frames: usize, 17 | } 18 | 19 | impl HugeBumpAllocator { 20 | pub fn new_custom(free_areas: Vec, kernel: MemoryArea, 21 | next_frame: HugeFrame) -> Self { 22 | let (total_frames, used_frames) = Self::calculate_frames(&free_areas, &kernel, &next_frame); 23 | HugeBumpAllocator { 24 | free_areas, 25 | next_free_area: 0, 26 | next_huge_frame: FrameIter::inclusive(next_frame.clone(), next_frame), 27 | kernel, 28 | total_frames, 29 | used_frames, 30 | } 31 | } 32 | 33 | fn calculate_frames(free_areas: &[MemoryArea], kernel: &MemoryArea, next_frame: &HugeFrame) -> (usize, usize) { 34 | let mut total_frames = 0; 35 | let mut used_frames = 0; 36 | 37 | for area in free_areas { 38 | let start = area.start_address().align_up(HugeFrame::SIZE); 39 | let end = area.end_address().align_down(HugeFrame::SIZE).raw(); 40 | let end = PhysicalAddress::new(end.saturating_sub(1)); 41 | let end_frame = HugeFrame::from_address(end); 42 | let start_frame = HugeFrame::from_address(start); 43 | total_frames += (end_frame.index() - start_frame.index()) + 1; 44 | 45 | if &start_frame <= next_frame && next_frame <= &end_frame { 46 | used_frames += (next_frame.index() - start_frame.index()) + 1; 47 | } 48 | } 49 | 50 | if kernel.start_address() > next_frame.to_memory_area().end_address() { 51 | let start = kernel.start_address().align_down(HugeFrame::SIZE); 52 | let end = kernel.end_address().align_up(HugeFrame::SIZE).raw(); 53 | let end = PhysicalAddress::new(end.saturating_sub(1)); 54 | let end_frame = HugeFrame::from_address(end); 55 | let start_frame = HugeFrame::from_address(start); 56 | used_frames += (end_frame.index() - start_frame.index()) + 1; 57 | } 58 | 59 | (total_frames, used_frames) 60 | } 61 | 62 | fn select_next_free_area(&mut self) -> Option> { 63 | let mut start: PhysicalAddress; 64 | let mut end: PhysicalAddress; 65 | 66 | { 67 | let next_free_area = self.free_areas.get(self.next_free_area)?; 68 | self.next_free_area += 1; 69 | 70 | start = next_free_area.start_address().align_up(HugeFrame::SIZE); 71 | let end_raw = next_free_area.end_address().align_down(HugeFrame::SIZE).raw(); 72 | end = PhysicalAddress::new(end_raw.saturating_sub(1)); 73 | } 74 | 75 | if self.next_huge_frame.previous_next().end_address() > start { 76 | start = self.next_huge_frame.previous_next().end_address().align_up(HugeFrame::SIZE); 77 | } 78 | 79 | if self.kernel.start_address() <= start && start <= self.kernel.end_address() { 80 | start = self.kernel.end_address().align_up(HugeFrame::SIZE); 81 | } 82 | 83 | if self.kernel.start_address() <= end && end <= self.kernel.end_address() { 84 | let end_raw = self.kernel.start_address().align_down(HugeFrame::SIZE).raw(); 85 | end = PhysicalAddress::new(end_raw.saturating_sub(1)); 86 | } 87 | 88 | if (end.raw().saturating_sub(start.raw())) < HugeFrame::SIZE { 89 | return self.select_next_free_area(); 90 | } 91 | 92 | Some(FrameIter::inclusive(HugeFrame::from_address(start), 93 | HugeFrame::from_address(end))) 94 | } 95 | } 96 | 97 | impl super::FrameLikeAllocator for HugeBumpAllocator { 98 | fn free_frames_count(&self) -> usize { 99 | self.total_frames - self.used_frames_count() 100 | } 101 | 102 | fn used_frames_count(&self) -> usize { 103 | self.used_frames 104 | } 105 | 106 | fn allocate(&mut self) -> Option { 107 | let frame = self.next_huge_frame.next(); 108 | if frame.is_none() { 109 | self.next_huge_frame = self.select_next_free_area()?; 110 | return self.allocate(); 111 | } 112 | self.used_frames += 1; 113 | frame 114 | } 115 | 116 | fn deallocate(&mut self, _frame: HugeFrame) { 117 | panic!("HugeBumpAllocator does not support deallocation of huge frames") 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /kernel/src/memory/huge_frame_allocators/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::huge_boot_bump_allocator::HugeBootBumpAllocator; 2 | pub use self::huge_bump_allocator::HugeBumpAllocator; 3 | use super::FrameLikeAllocator; 4 | 5 | pub mod huge_boot_bump_allocator; 6 | pub mod huge_bump_allocator; -------------------------------------------------------------------------------- /kernel/src/memory/memory_area.rs: -------------------------------------------------------------------------------- 1 | use super::PhysicalAddress; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct MemoryArea { 5 | start: PhysicalAddress, 6 | size: usize, 7 | } 8 | 9 | impl MemoryArea { 10 | pub fn new(start: PhysicalAddress, size: usize) -> MemoryArea { 11 | MemoryArea { 12 | start, 13 | size, 14 | } 15 | } 16 | 17 | pub fn start_address(&self) -> PhysicalAddress { 18 | self.start.clone() 19 | } 20 | 21 | pub fn end_address(&self) -> PhysicalAddress { 22 | PhysicalAddress::new(self.start.raw() + self.size() as u64) 23 | } 24 | 25 | pub fn size(&self) -> usize { 26 | self.size 27 | } 28 | 29 | pub fn overlap(&self, other: &MemoryArea) -> Option { 30 | let start = self.start_address().raw().max(other.start_address().raw()); 31 | let end = self.end_address().raw().min(other.end_address().raw()); 32 | let size = end.checked_sub(start)? as usize; 33 | Some(MemoryArea::new(PhysicalAddress::new(start), size)) 34 | } 35 | } 36 | 37 | impl<'a> From<&'a ::multiboot2::MemoryArea> for MemoryArea { 38 | fn from(other: &::multiboot2::MemoryArea) -> Self { 39 | let start = PhysicalAddress::new(other.start_address() as u64); 40 | MemoryArea::new(start, other.size()) 41 | } 42 | } 43 | 44 | impl<'a> From<&'a ::multiboot2::ModuleTag> for MemoryArea { 45 | fn from(other: &::multiboot2::ModuleTag) -> Self { 46 | let start = PhysicalAddress::new(other.start_address() as u64); 47 | MemoryArea::new(start, (other.end_address() - other.start_address()) as usize) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kernel/src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::address::PhysicalAddress; 2 | pub use self::frame::Frame; 3 | pub use self::frame::FrameIter; 4 | pub use self::frame::FrameLike; 5 | pub use self::frame::HugeFrame; 6 | pub use self::frame_allocator::FrameLikeAllocator; 7 | pub use self::functions::FRAME_ALLOCATOR; 8 | pub use self::generic_allocators::GenericAllocator; 9 | pub use self::memory_area::MemoryArea; 10 | 11 | pub mod frame_allocators; 12 | pub mod huge_frame_allocators; 13 | pub mod frame_allocator; 14 | pub mod frame; 15 | pub mod address; 16 | pub mod memory_area; 17 | pub mod generic_allocators; 18 | pub mod functions; 19 | -------------------------------------------------------------------------------- /kernel/src/paging/active_page_table.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::FrameLike; 3 | use memory::FrameLikeAllocator; 4 | use memory::PhysicalAddress; 5 | use super::InactivePageTable; 6 | use super::PageMapper; 7 | use super::TemporaryPage; 8 | 9 | pub struct ActivePageTable { 10 | page_mapper: PageMapper, 11 | } 12 | 13 | impl ActivePageTable { 14 | /// Creates an interface over the current page table 15 | /// 16 | /// # Safety 17 | /// 18 | /// Only one `ActivePageTable` instance should exist 19 | /// 20 | pub unsafe fn new() -> ActivePageTable { 21 | ActivePageTable { 22 | page_mapper: PageMapper::new(), 23 | } 24 | } 25 | 26 | pub fn with(&mut self, inactive_table: &mut InactivePageTable, 27 | allocator: &mut FrameLikeAllocator, f: F) -> R 28 | where F: FnOnce(&mut PageMapper, &mut FrameLikeAllocator) -> R { 29 | use super::EntryFlags; 30 | use super::PageLike; 31 | use memory::frame_allocators::TinyAllocator; 32 | use x86_64::instructions::tlb::flush_all; 33 | 34 | let tiny_allocator = TinyAllocator::new(allocator); 35 | let page = super::Page::from_address(::paging::reserved::ACTIVE_TABLE_WITH_TEMPORARY_PAGE); 36 | let mut temporary_page = TemporaryPage::new(page, tiny_allocator); 37 | let active_table_frame = Frame::from_address(self.current_table_address()); 38 | 39 | let value; 40 | { 41 | let active_table = temporary_page.map_table_frame(active_table_frame.clone(), self); 42 | 43 | self.table_mut()[511].set(inactive_table.table_root().clone(), EntryFlags::PRESENT | EntryFlags::WRITABLE); 44 | flush_all(); 45 | value = f(self, allocator); 46 | 47 | active_table[511].set(active_table_frame, EntryFlags::PRESENT | EntryFlags::WRITABLE); 48 | flush_all(); 49 | } 50 | 51 | temporary_page.discard(self); 52 | temporary_page.unwrap().dispose(allocator); 53 | value 54 | } 55 | 56 | pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable { 57 | let current_table_frame = Frame::from_address(self.current_table_address()); 58 | let current_table = unsafe { InactivePageTable::new_mapped(current_table_frame) }; 59 | unsafe { 60 | let new_table_address = new_table.table_root().start_address().raw(); 61 | let new_table_address = ::x86_64::PhysicalAddress(new_table_address); 62 | ::x86_64::registers::control_regs::cr3_write(new_table_address); 63 | } 64 | current_table 65 | } 66 | 67 | pub fn current_table_address(&self) -> PhysicalAddress { 68 | let active_table_address = ::x86_64::registers::control_regs::cr3(); 69 | PhysicalAddress::new(active_table_address.0) 70 | } 71 | } 72 | 73 | impl ::core::ops::Deref for ActivePageTable { 74 | type Target = PageMapper; 75 | 76 | fn deref(&self) -> &Self::Target { 77 | &self.page_mapper 78 | } 79 | } 80 | 81 | impl ::core::ops::DerefMut for ActivePageTable { 82 | fn deref_mut(&mut self) -> &mut PageMapper { 83 | &mut self.page_mapper 84 | } 85 | } -------------------------------------------------------------------------------- /kernel/src/paging/address.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] 2 | pub struct VirtualAddress(usize); 3 | 4 | impl VirtualAddress { 5 | pub const fn new(raw: usize) -> VirtualAddress { 6 | VirtualAddress(raw) 7 | } 8 | 9 | /// Creates a new VirtualAddress in the higher half 10 | pub const fn new_adjusted(raw: usize) -> VirtualAddress { 11 | VirtualAddress::new(raw + ::KERNEL_BASE as usize) 12 | } 13 | 14 | pub const fn raw(&self) -> usize { 15 | self.0 16 | } 17 | 18 | pub const fn offset(&self, offset: usize) -> VirtualAddress { 19 | VirtualAddress::new(self.raw() + offset) 20 | } 21 | } 22 | 23 | impl ::core::fmt::Debug for VirtualAddress { 24 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { 25 | write!(f, "{:#x}", self.raw()) 26 | } 27 | } 28 | 29 | impl From<::x86_64::VirtualAddress> for VirtualAddress { 30 | fn from(address: ::x86_64::VirtualAddress) -> Self { 31 | Self::new(address.0) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /kernel/src/paging/functions.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::FrameIter; 3 | use memory::FrameLike; 4 | use memory::FrameLikeAllocator; 5 | use memory::PhysicalAddress; 6 | use multiboot2::BootInformation; 7 | use super::ActivePageTable; 8 | use super::EntryFlags; 9 | use super::InactivePageTable; 10 | use super::Page; 11 | use super::PageLike; 12 | use super::PageMapper; 13 | use super::VirtualAddress; 14 | use utility::Global; 15 | 16 | pub static ACTIVE_PAGE_TABLE: Global = Global::new("ACTIVE_PAGE_TABLE"); 17 | 18 | pub fn initialize(boot_information: &BootInformation, allocator: &mut A) -> InactivePageTable 19 | where A: FrameLikeAllocator { 20 | let _status = ::display::text_mode::BootStatus::new("Remapping kernel memory sections"); 21 | 22 | let _elf_sections_tag = boot_information.elf_sections_tag().expect("ElfSectionsTag required"); 23 | let _memory_map_tag = boot_information.memory_map_tag().expect("MemoryMapTag required"); 24 | 25 | enable_cpu_features(); 26 | let base_table = remap_kernel(boot_information, allocator); 27 | 28 | ACTIVE_PAGE_TABLE.set(unsafe { ActivePageTable::new() }); 29 | base_table 30 | } 31 | 32 | pub unsafe fn as_table_root<'a>(table_root: Page) -> &'a mut super::PageTable { 33 | &mut *(table_root.start_address().raw() as *mut super::PageTable) 34 | } 35 | 36 | fn enable_cpu_features() { 37 | enable_nxe_bit(); 38 | enable_write_protect_bit(); 39 | enable_global_pages_bit(); 40 | } 41 | 42 | fn enable_nxe_bit() { 43 | // Allows NO_EXECUTE to be marked on pages 44 | 45 | use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr}; 46 | const NXE_BIT: u64 = 1 << 11; 47 | unsafe { 48 | let efer = rdmsr(IA32_EFER); 49 | wrmsr(IA32_EFER, efer | NXE_BIT); 50 | } 51 | } 52 | 53 | fn enable_write_protect_bit() { 54 | use x86_64::registers::control_regs::{cr0, cr0_write, Cr0}; 55 | unsafe { cr0_write(cr0() | Cr0::WRITE_PROTECT) }; 56 | } 57 | 58 | fn enable_global_pages_bit() { 59 | // Global pages aren't actually used anywhere, 60 | // but they can be used for marking kernel pages 61 | // to prevent those entries from being flushed 62 | // on context switches 63 | 64 | use x86_64::registers::control_regs::{cr4, cr4_write, Cr4}; 65 | unsafe { cr4_write(cr4() | Cr4::ENABLE_GLOBAL_PAGES) }; 66 | } 67 | 68 | fn remap_kernel(boot_information: &BootInformation, allocator: &mut A) -> InactivePageTable 69 | where A: FrameLikeAllocator { 70 | let mut active_table = unsafe { ActivePageTable::new() }; 71 | 72 | let mut remap_table = { 73 | let frame = allocator.allocate().expect("Out of memory: Kernel remap"); 74 | InactivePageTable::new_cleared(frame, &mut active_table, allocator) 75 | }; 76 | 77 | // We do not map the multiboot sections here because they are 78 | // not usable once we finish booting 79 | active_table.with(&mut remap_table, allocator, |mapper, allocator| { 80 | remap_vga_buffer(mapper, allocator); 81 | remap_kernel_sections(boot_information, mapper, allocator); 82 | prepare_kernel_page_directories(mapper, allocator); 83 | }); 84 | let base_table = remap_table.clone_shallow(&mut active_table, allocator); 85 | 86 | active_table.with(&mut remap_table, allocator, |mapper, allocator| { 87 | remap_multiboot(boot_information, mapper, allocator); 88 | }); 89 | 90 | // We switch to the newly created page table 91 | let old_table = active_table.switch(remap_table); 92 | create_guard_page(old_table, &mut active_table, allocator); 93 | base_table 94 | } 95 | 96 | fn create_guard_page(old_table: InactivePageTable, mapper: &mut PageMapper, allocator: &mut A) 97 | where A: FrameLikeAllocator { 98 | let old_table_raw_address = old_table.table_root().start_address().raw(); 99 | let old_table_address = VirtualAddress::new_adjusted(old_table_raw_address as usize); 100 | 101 | // The old page table is directly located below our kernel stack 102 | // Thus, if we un map the old page table, if we overflow our 103 | // stack, a page fault will occur. 104 | let guard_page = Page::from_address(old_table_address); 105 | let frame = mapper.un_map(guard_page, allocator); 106 | allocator.deallocate(frame); 107 | } 108 | 109 | fn remap_kernel_sections(boot_info: &BootInformation, mapper: &mut PageMapper, allocator: &mut FrameLikeAllocator) { 110 | let elf_sections = boot_info.elf_sections_tag().unwrap(); 111 | for section in elf_sections.sections() { 112 | // If the section isn't allocated, then it doesn't exist in memory 113 | if !section.is_allocated() { 114 | continue; 115 | } 116 | 117 | let flags = EntryFlags::kernel_elf_section(§ion); 118 | 119 | // For this to work correctly, all kernel sections have to be aligned 120 | // on a page boundary. To use a HugeFrame, all sections have to be 121 | // aligned on a HugeFrame boundary. Since the kernel is so small, 122 | // it is not worth using HugeFrames. See linker.ld 123 | let start_frame = Frame::from_address(PhysicalAddress::new_adjusted(section.start_address())); 124 | let end_frame = Frame::from_address(PhysicalAddress::new_adjusted(section.end_address() - 1)); 125 | 126 | for frame in FrameIter::inclusive_unchecked(start_frame, end_frame) { 127 | let page = Page::from_address(VirtualAddress::new_adjusted(frame.start_address().raw() as usize)); 128 | mapper.map_to(page, frame, flags, allocator); 129 | } 130 | } 131 | } 132 | 133 | fn remap_vga_buffer(mapper: &mut PageMapper, allocator: &mut FrameLikeAllocator) { 134 | // We access the VGA buffer using a higher half kernel page 135 | let vga_buffer_page = Page::from_address(VirtualAddress::new_adjusted(0xb8000)); 136 | 137 | // However, the actual VGA buffer is still located in the lower half 138 | // of physical memory 139 | let vga_buffer_frame = Frame::from_address(PhysicalAddress::new(0xb8000)); 140 | mapper.map_to(vga_buffer_page, vga_buffer_frame, EntryFlags::WRITABLE, allocator); 141 | } 142 | 143 | fn remap_multiboot(boot_info: &BootInformation, mapper: &mut PageMapper, allocator: &mut FrameLikeAllocator) { 144 | let multiboot_start = Frame::from_address(PhysicalAddress::new_force_adjust(boot_info.start_address() as u64)); 145 | let multiboot_end = Frame::from_address(PhysicalAddress::new_force_adjust(boot_info.end_address() as u64 - 1)); 146 | for frame in FrameIter::inclusive(multiboot_start, multiboot_end) { 147 | let page = Page::from_address(VirtualAddress::new_adjusted(frame.start_address().raw() as usize)); 148 | mapper.map_to(page, frame, EntryFlags::empty(), allocator); 149 | } 150 | } 151 | 152 | // Addresses from 0xF000_0000_0000 upwards are dedicated to the kernel, 153 | // so we must ensure that the page directories for those addresses 154 | // exist so we can clone the entire page table safely 155 | fn prepare_kernel_page_directories(mapper: &mut PageMapper, allocator: &mut FrameLikeAllocator) { 156 | for index in 480..510 { 157 | mapper.table_mut().create_if_nonexistent(index, allocator); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /kernel/src/paging/inactive_page_table.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::frame_allocators::TinyAllocator; 3 | use memory::FrameLikeAllocator; 4 | use super::EntryFlags; 5 | use super::Page; 6 | use super::PageLike; 7 | use super::PageMapper; 8 | use super::TemporaryPage; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct InactivePageTable { 12 | table_root: Frame, 13 | } 14 | 15 | impl InactivePageTable { 16 | pub fn new_cleared(table_root: Frame, page_mapper: &mut PageMapper, 17 | allocator: &mut FrameLikeAllocator) -> InactivePageTable { 18 | let tiny_allocator = TinyAllocator::new(allocator); 19 | let reserved_page = Page::from_address(super::reserved::TEMPORARY_PAGE); 20 | let mut temporary_page = TemporaryPage::new(reserved_page, tiny_allocator); 21 | 22 | { 23 | let table = temporary_page.map_table_frame(table_root.clone(), page_mapper); 24 | table.clear(); 25 | table[511].set(table_root.clone(), EntryFlags::PRESENT | EntryFlags::WRITABLE); 26 | } 27 | temporary_page.discard(page_mapper); 28 | unsafe { InactivePageTable::new_mapped(table_root) } 29 | } 30 | 31 | pub fn clone_shallow(&self, page_mapper: &mut PageMapper, 32 | allocator: &mut FrameLikeAllocator) -> InactivePageTable { 33 | use super::PageTable; 34 | use super::table_level::Level4; 35 | 36 | // We use a temporary page to access this inactive table's page table root 37 | let original_page = Page::from_address(super::reserved::CLONE_SHALLOW_TEMPORARY_PAGE); 38 | page_mapper.map_to(original_page.clone(), self.table_root.clone(), EntryFlags::empty(), allocator); 39 | 40 | // We allocate a frame to store the cloned page table root 41 | let table_frame = allocator.allocate().expect("Out of memory: PAGE_TABLE_SHALLOW_CLONE"); 42 | let clone_page = Page::from_address(super::reserved::TEMPORARY_PAGE); 43 | page_mapper.map_to(clone_page.clone(), table_frame.clone(), EntryFlags::WRITABLE, allocator); 44 | 45 | unsafe { 46 | let original_table = original_page.start_address().raw() as *const PageTable; 47 | let clone_table = clone_page.start_address().raw() as *mut PageTable; 48 | ::core::ptr::copy(original_table, clone_table, 1); 49 | } 50 | 51 | // We need to remap the recursive mapping as it still points 52 | // to the same frame as the original table. 53 | let table = unsafe { super::functions::as_table_root(clone_page.clone()) }; 54 | table[511].set(table_frame.clone(), EntryFlags::PRESENT | EntryFlags::WRITABLE); 55 | 56 | // The temporary page mappings are no longer needed 57 | page_mapper.discard(original_page, allocator); 58 | page_mapper.discard(clone_page, allocator); 59 | unsafe { InactivePageTable::new_mapped(table_frame) } 60 | } 61 | 62 | /// Creates an interface over an existing PageTable 63 | /// 64 | /// # Safety 65 | /// 66 | /// The PageTable must be recursively mapped 67 | /// 68 | pub unsafe fn new_mapped(table_root: Frame) -> InactivePageTable { 69 | InactivePageTable { 70 | table_root, 71 | } 72 | } 73 | 74 | pub fn table_root(&self) -> &Frame { 75 | &self.table_root 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /kernel/src/paging/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::active_page_table::ActivePageTable; 2 | pub use self::address::VirtualAddress; 3 | pub use self::functions::ACTIVE_PAGE_TABLE; 4 | pub use self::inactive_page_table::InactivePageTable; 5 | pub use self::page::HugePage; 6 | pub use self::page::Page; 7 | pub use self::page::PageLike; 8 | pub use self::page_entry::EntryFlags; 9 | use self::page_entry::PageEntry; 10 | pub use self::page_iter::PageIter; 11 | use self::page_mapper::PageMapper; 12 | use self::page_table::PageTable; 13 | use self::table_level::HierarchicalLevel; 14 | use self::table_level::TableLevel; 15 | pub use self::temporary_page::TemporaryPage; 16 | 17 | pub mod page; 18 | pub mod page_table; 19 | pub mod table_level; 20 | pub mod page_entry; 21 | pub mod active_page_table; 22 | pub mod address; 23 | pub mod inactive_page_table; 24 | pub mod temporary_page; 25 | pub mod page_mapper; 26 | pub mod functions; 27 | pub mod reserved; 28 | pub mod page_iter; -------------------------------------------------------------------------------- /kernel/src/paging/page.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::FrameLike; 3 | use memory::FrameLikeAllocator; 4 | use memory::HugeFrame; 5 | use super::EntryFlags; 6 | use super::PageMapper; 7 | use super::VirtualAddress; 8 | 9 | pub trait PageLike: Clone { 10 | const SIZE: u64; 11 | type FrameType: FrameLike; 12 | 13 | fn from_index(index: usize) -> Self where Self: Sized; 14 | fn index(&self) -> usize; 15 | fn map_to(&self, table: &mut PageMapper, frame: Self::FrameType, 16 | flags: EntryFlags, allocator: &mut FrameLikeAllocator); 17 | fn un_map(&self, table: &mut PageMapper, allocator: &mut FrameLikeAllocator) -> Self::FrameType; 18 | 19 | fn from_address(address: VirtualAddress) -> Self where Self: Sized { 20 | Self::from_index(address.raw() / Self::SIZE as usize) 21 | } 22 | 23 | fn start_address(&self) -> VirtualAddress where Self: Sized { 24 | VirtualAddress::new(self.index() * Self::SIZE as usize) 25 | } 26 | 27 | fn end_address(&self) -> VirtualAddress where Self: Sized { 28 | VirtualAddress::new((self.index() * Self::SIZE as usize) + (Self::SIZE as usize - 1)) 29 | } 30 | } 31 | 32 | #[derive(Debug, Clone)] 33 | pub struct Page { 34 | index: usize, 35 | } 36 | 37 | impl Page { 38 | pub fn table_4_index(&self) -> usize { 39 | (self.index >> 27) & 0o777 40 | } 41 | 42 | pub fn table_3_index(&self) -> usize { 43 | (self.index >> 18) & 0o777 44 | } 45 | 46 | pub fn table_2_index(&self) -> usize { 47 | (self.index >> 9) & 0o777 48 | } 49 | 50 | pub fn table_1_index(&self) -> usize { 51 | (self.index >> 0) & 0o777 52 | } 53 | } 54 | 55 | impl PageLike for Page { 56 | const SIZE: u64 = 4 * 1024; 57 | type FrameType = ::memory::Frame; 58 | 59 | fn from_index(index: usize) -> Self where Self: Sized { 60 | Self { 61 | index, 62 | } 63 | } 64 | 65 | fn index(&self) -> usize { 66 | self.index 67 | } 68 | 69 | fn map_to(&self, table: &mut PageMapper, frame: Self::FrameType, 70 | flags: EntryFlags, allocator: &mut FrameLikeAllocator) { 71 | let table_4 = table.table_mut(); 72 | let table_3 = table_4.create_if_nonexistent(self.table_4_index(), allocator); 73 | let table_2 = table_3.create_if_nonexistent(self.table_3_index(), allocator); 74 | let table_1 = table_2.create_if_nonexistent(self.table_2_index(), allocator); 75 | 76 | assert!(table_1[self.table_1_index()].is_unused(), "Page at {:?} is already mapped", self.start_address()); 77 | table_1[self.table_1_index()].set(frame, flags); 78 | } 79 | 80 | fn un_map(&self, table: &mut PageMapper, _allocator: &mut FrameLikeAllocator) -> Frame { 81 | assert!(table.translate(&self.start_address()).is_some()); 82 | 83 | let table_4 = table.table_mut(); 84 | let table_1 = table_4.next_table_mut(self.table_4_index()) 85 | .and_then(|table_3| table_3.next_table_mut(self.table_3_index())) 86 | .and_then(|table_2| table_2.next_table_mut(self.table_2_index())) 87 | .expect("Cannot remove mapping of huge page from normal page handler"); 88 | let frame: Frame = table_1[self.table_1_index()].frame().unwrap(); 89 | table_1[self.table_1_index()].set_unused(); 90 | frame 91 | } 92 | } 93 | 94 | #[derive(Debug, Clone)] 95 | pub struct HugePage { 96 | index: usize, 97 | } 98 | 99 | impl HugePage { 100 | fn table_4_index(&self) -> usize { 101 | (self.index >> 18) & 0o777 102 | } 103 | 104 | fn table_3_index(&self) -> usize { 105 | (self.index >> 9) & 0o777 106 | } 107 | 108 | fn table_2_index(&self) -> usize { 109 | (self.index >> 0) & 0o777 110 | } 111 | } 112 | 113 | impl PageLike for HugePage { 114 | const SIZE: u64 = 2 * 1024 * 1024; 115 | type FrameType = ::memory::HugeFrame; 116 | 117 | fn from_index(index: usize) -> Self where Self: Sized { 118 | Self { 119 | index, 120 | } 121 | } 122 | 123 | fn index(&self) -> usize { 124 | self.index 125 | } 126 | 127 | fn map_to(&self, table: &mut PageMapper, frame: Self::FrameType, 128 | flags: EntryFlags, allocator: &mut FrameLikeAllocator) { 129 | let table_4 = table.table_mut(); 130 | let table_3 = table_4.create_if_nonexistent(self.table_4_index(), allocator); 131 | let table_2 = table_3.create_if_nonexistent(self.table_3_index(), allocator); 132 | 133 | assert!(table_2[self.table_2_index()].is_unused(), "Huge page at {:?} is already mapped", self.start_address()); 134 | table_2[self.table_2_index()].set(frame, flags | EntryFlags::HUGE_PAGE); 135 | } 136 | 137 | fn un_map(&self, table: &mut PageMapper, _allocator: &mut FrameLikeAllocator) -> HugeFrame { 138 | assert!(table.translate(&self.start_address()).is_some()); 139 | 140 | let table_4 = table.table_mut(); 141 | let table_2 = table_4.next_table_mut(self.table_4_index()) 142 | .and_then(|table_3| table_3.next_table_mut(self.table_3_index())) 143 | .unwrap(); 144 | let frame: HugeFrame = table_2[self.table_2_index()].frame().unwrap(); 145 | table_2[self.table_2_index()].set_unused(); 146 | frame 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /kernel/src/paging/page_entry.rs: -------------------------------------------------------------------------------- 1 | use memory::FrameLike; 2 | use memory::PhysicalAddress; 3 | 4 | pub struct PageEntry(u64); 5 | 6 | impl PageEntry { 7 | const ADDRESS_MASK: u64 = 0x000f_ffff_ffff_f000; 8 | 9 | pub fn raw(&self) -> u64 { 10 | self.0 11 | } 12 | 13 | fn set_raw(&mut self, raw: u64) { 14 | self.0 = raw; 15 | } 16 | 17 | pub fn is_unused(&self) -> bool { 18 | self.raw() == 0 19 | } 20 | 21 | pub fn set_unused(&mut self) { 22 | self.set_raw(0); 23 | } 24 | 25 | pub fn flags(&self) -> EntryFlags { 26 | EntryFlags::from_bits_truncate(self.raw()) 27 | } 28 | 29 | fn address(&self) -> PhysicalAddress { 30 | PhysicalAddress::new(self.raw() & Self::ADDRESS_MASK) 31 | } 32 | 33 | pub fn frame(&self) -> Option { 34 | if self.flags().contains(EntryFlags::PRESENT) { 35 | return Some(F::from_address(self.address())); 36 | } 37 | None 38 | } 39 | 40 | pub fn set(&mut self, frame: F, flags: EntryFlags) { 41 | assert_eq!(frame.start_address().raw() & !Self::ADDRESS_MASK, 0); 42 | self.set_raw(frame.start_address().raw() as u64 | flags.bits()); 43 | } 44 | } 45 | 46 | bitflags! { 47 | pub struct EntryFlags: u64 { 48 | const PRESENT = 1 << 0; 49 | const WRITABLE = 1 << 1; 50 | const USER_ACCESSIBLE = 1 << 2; 51 | const WRITE_THROUGH = 1 << 3; 52 | const NO_CACHE = 1 << 4; 53 | const ACCESSED = 1 << 5; 54 | const DIRTY = 1 << 6; 55 | const HUGE_PAGE = 1 << 7; 56 | const GLOBAL = 1 << 8; 57 | const NO_EXECUTE = 1 << 63; 58 | } 59 | } 60 | 61 | impl EntryFlags { 62 | pub fn kernel_elf_section(section: &::multiboot2::ElfSection) -> EntryFlags { 63 | use multiboot2::ElfSectionFlags; 64 | let mut flags = EntryFlags::empty(); 65 | 66 | if section.flags().contains(ElfSectionFlags::ALLOCATED) { 67 | flags |= Self::PRESENT; 68 | } 69 | 70 | if section.flags().contains(ElfSectionFlags::WRITABLE) { 71 | flags |= Self::WRITABLE; 72 | } 73 | 74 | if !section.flags().contains(ElfSectionFlags::EXECUTABLE) { 75 | flags |= Self::NO_EXECUTE; 76 | } 77 | 78 | flags 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /kernel/src/paging/page_iter.rs: -------------------------------------------------------------------------------- 1 | use super::PageLike; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct PageIter { 5 | start: usize, 6 | next: usize, 7 | end: usize, 8 | _page_type: ::core::marker::PhantomData

, 9 | } 10 | 11 | impl PageIter

{ 12 | pub fn inclusive(start: P, end: P) -> PageIter

{ 13 | assert!(start.index() <= end.index()); 14 | PageIter { 15 | start: start.index(), 16 | next: start.index(), 17 | end: end.index(), 18 | _page_type: Default::default(), 19 | } 20 | } 21 | } 22 | 23 | impl Iterator for PageIter

{ 24 | type Item = P; 25 | 26 | fn next(&mut self) -> Option<::Item> { 27 | if self.next <= self.end { 28 | self.next += 1; 29 | Some(P::from_index(self.next - 1)) 30 | } else { 31 | None 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kernel/src/paging/page_mapper.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::FrameLike; 3 | use memory::FrameLikeAllocator; 4 | use memory::HugeFrame; 5 | use memory::PhysicalAddress; 6 | use super::EntryFlags; 7 | use super::Page; 8 | use super::PageLike; 9 | use super::PageTable; 10 | use super::table_level::Level4; 11 | use super::VirtualAddress; 12 | 13 | pub struct PageMapper { 14 | table_root: &'static mut PageTable, 15 | } 16 | 17 | impl PageMapper { 18 | const TABLE_4_ADDRESS: u64 = 0xffff_ffff_ffff_f000; 19 | 20 | /// Creates an interface over the level four page table 21 | /// 22 | /// # Safety 23 | /// 24 | /// The page table should be recursively mapped 25 | /// Only one `PageMapper` instance should exist 26 | /// 27 | pub unsafe fn new() -> PageMapper { 28 | PageMapper { 29 | table_root: &mut *((PageMapper::TABLE_4_ADDRESS) as *mut PageTable), 30 | } 31 | } 32 | 33 | pub fn table(&self) -> &PageTable { 34 | self.table_root 35 | } 36 | 37 | pub fn table_mut(&mut self) -> &mut PageTable { 38 | self.table_root 39 | } 40 | 41 | pub fn map_to

(&mut self, page: P, frame: P::FrameType, flags: EntryFlags, 42 | allocator: &mut FrameLikeAllocator) where P: PageLike { 43 | page.map_to(self, frame, flags | EntryFlags::PRESENT, allocator); 44 | } 45 | 46 | #[must_use] 47 | pub fn un_map

(&mut self, page: P, allocator: &mut FrameLikeAllocator) 48 | -> P::FrameType where P: PageLike { 49 | let frame = page.un_map(self, allocator); 50 | self.flush_table_entry(&page); 51 | frame 52 | } 53 | 54 | pub fn discard

(&mut self, page: P, allocator: &mut FrameLikeAllocator) where P: PageLike { 55 | let _ = self.un_map(page, allocator); 56 | } 57 | 58 | pub fn translate(&self, address: &VirtualAddress) -> Option { 59 | let page = Page::from_address(address.clone()); 60 | let table_2 = self.table().next_table(page.table_4_index()) 61 | .and_then(|table_3| table_3.next_table(page.table_3_index()))?; 62 | 63 | if table_2[page.table_2_index()].flags().contains(EntryFlags::HUGE_PAGE) { 64 | let huge_frame: HugeFrame = table_2[page.table_2_index()].frame()?; 65 | let raw_address = huge_frame.start_address().raw() + (address.raw() as u64 % HugeFrame::SIZE); 66 | return Some(PhysicalAddress::new(raw_address)); 67 | } 68 | 69 | let frame: Frame = table_2.next_table(page.table_2_index()) 70 | .and_then(|table_1| table_1[page.table_1_index()].frame())?; 71 | let raw_address = frame.start_address().raw() + (address.raw() as u64 % Frame::SIZE); 72 | Some(PhysicalAddress::new(raw_address)) 73 | } 74 | 75 | pub fn flush_table_entry

(&mut self, page: &P) where P: PageLike { 76 | use x86_64::VirtualAddress; 77 | ::x86_64::instructions::tlb::flush(VirtualAddress(page.start_address().raw())); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /kernel/src/paging/page_table.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::FrameLikeAllocator; 3 | use super::EntryFlags; 4 | use super::PageEntry; 5 | use super::TableLevel; 6 | use super::VirtualAddress; 7 | 8 | const ENTRY_COUNT: usize = 512; 9 | 10 | pub struct PageTable { 11 | entries: [PageEntry; ENTRY_COUNT], 12 | _level: ::core::marker::PhantomData, 13 | } 14 | 15 | impl PageTable where L: TableLevel { 16 | pub fn clear(&mut self) { 17 | for entry in self.entries.iter_mut() { 18 | entry.set_unused(); 19 | } 20 | } 21 | } 22 | 23 | impl PageTable where L: super::HierarchicalLevel { 24 | fn next_table_address(&self, index: usize) -> Option { 25 | let entry_flags = self[index].flags(); 26 | if entry_flags.contains(EntryFlags::PRESENT) && !entry_flags.contains(EntryFlags::HUGE_PAGE) { 27 | let table_address = self as *const _ as usize; 28 | return Some(VirtualAddress::new((table_address << 9) | (index << 12))); 29 | } 30 | None 31 | } 32 | 33 | pub fn next_table(&self, index: usize) -> Option<&PageTable> { 34 | self.next_table_address(index).map(|address| unsafe { &*(address.raw() as *const _) }) 35 | } 36 | 37 | pub fn next_table_mut(&mut self, index: usize) -> Option<&mut PageTable> { 38 | self.next_table_address(index).map(|address| unsafe { &mut *(address.raw() as *mut _) }) 39 | } 40 | 41 | pub fn create_if_nonexistent(&mut self, index: usize, allocator: &mut FrameLikeAllocator) -> &mut PageTable { 42 | if self.next_table_mut(index).is_none() { 43 | let table_frame = allocator.allocate().expect("Out of memory: PageTable entry"); 44 | 45 | // Note well: The flags must be USER_ACCESSIBLE because all the 46 | // page directories have to be USER_ACCESSIBLE for a user mode 47 | // thread to access a USER_ACCESSIBLE page 48 | let flags = EntryFlags::PRESENT | EntryFlags::WRITABLE | EntryFlags::USER_ACCESSIBLE; 49 | self.entries[index].set(table_frame, flags); 50 | self.next_table_mut(index).unwrap().clear() 51 | } 52 | 53 | self.next_table_mut(index).unwrap() 54 | } 55 | } 56 | 57 | impl ::core::ops::Index for PageTable where L: TableLevel { 58 | type Output = PageEntry; 59 | 60 | fn index(&self, index: usize) -> &PageEntry { 61 | &self.entries[index] 62 | } 63 | } 64 | 65 | impl ::core::ops::IndexMut for PageTable where L: TableLevel { 66 | fn index_mut(&mut self, index: usize) -> &mut PageEntry { 67 | &mut self.entries[index] 68 | } 69 | } -------------------------------------------------------------------------------- /kernel/src/paging/reserved.rs: -------------------------------------------------------------------------------- 1 | use super::PageLike; 2 | use super::VirtualAddress; 3 | 4 | // This files collates all the special addresses that are used in the kernel 5 | // This makes it easy to see which areas of memory are already used for 6 | // some function 7 | 8 | // Note well: The first sixteen bits of a virtual address must match 9 | // the 17th bit (from the left) due to the intel memory hole. 10 | // Otherwise, accessing the address will cause a General Protection Fault 11 | pub const TEMPORARY_PAGE: VirtualAddress = VirtualAddress::new(0xffff_f000_0000_1000); 12 | pub const ACTIVE_TABLE_WITH_TEMPORARY_PAGE: VirtualAddress = TEMPORARY_PAGE.offset(0x1000); 13 | pub const CLONE_SHALLOW_TEMPORARY_PAGE: VirtualAddress = ACTIVE_TABLE_WITH_TEMPORARY_PAGE.offset(0x1000); 14 | pub const HUGE_TEMPORARY_PAGE: VirtualAddress = VirtualAddress::new(0xffff_f000_1000_0000); 15 | 16 | pub const HEAP_SIZE: usize = 0x0100_0000_0000 - 1; 17 | pub const HEAP_BOTTOM: VirtualAddress = VirtualAddress::new(0xffff_f100_0000_0000); 18 | pub const HEAP_TOP: VirtualAddress = HEAP_BOTTOM.offset(HEAP_SIZE); 19 | 20 | pub const FRAME_STORE_SIZE: usize = 0x0080_0000_0000 - 1; 21 | pub const FRAME_STORE_BOTTOM: VirtualAddress = HEAP_TOP.offset(1); 22 | pub const FRAME_STORE_TOP: VirtualAddress = FRAME_STORE_BOTTOM.offset(FRAME_STORE_SIZE); 23 | pub const HUGE_FRAME_STORE_BOTTOM: VirtualAddress = FRAME_STORE_TOP.offset(1); 24 | pub const HUGE_FRAME_STORE_TOP: VirtualAddress = HUGE_FRAME_STORE_BOTTOM.offset(FRAME_STORE_SIZE); 25 | 26 | pub const TASK_SWITCH_STACK_SIZE: usize = super::Page::SIZE as usize - 1; 27 | pub const TASK_SWITCH_STACK_BOTTOM: VirtualAddress = HUGE_FRAME_STORE_TOP.offset(1); 28 | pub const TASK_SWITCH_STACK_TOP: VirtualAddress = TASK_SWITCH_STACK_BOTTOM.offset(TASK_SWITCH_STACK_SIZE); 29 | -------------------------------------------------------------------------------- /kernel/src/paging/table_level.rs: -------------------------------------------------------------------------------- 1 | pub trait TableLevel {} 2 | 3 | pub enum Level4 {} 4 | 5 | pub enum Level3 {} 6 | 7 | pub enum Level2 {} 8 | 9 | pub enum Level1 {} 10 | 11 | impl TableLevel for Level4 {} 12 | 13 | impl TableLevel for Level3 {} 14 | 15 | impl TableLevel for Level2 {} 16 | 17 | impl TableLevel for Level1 {} 18 | 19 | pub trait HierarchicalLevel: TableLevel { 20 | type NextLevel: TableLevel; 21 | } 22 | 23 | impl HierarchicalLevel for Level4 { 24 | type NextLevel = Level3; 25 | } 26 | 27 | impl HierarchicalLevel for Level3 { 28 | type NextLevel = Level2; 29 | } 30 | 31 | impl HierarchicalLevel for Level2 { 32 | type NextLevel = Level1; 33 | } 34 | -------------------------------------------------------------------------------- /kernel/src/paging/temporary_page.rs: -------------------------------------------------------------------------------- 1 | use memory::Frame; 2 | use memory::frame_allocators::TinyAllocator; 3 | use super::Page; 4 | use super::PageMapper; 5 | use super::PageTable; 6 | use super::table_level::Level1; 7 | use super::VirtualAddress; 8 | 9 | pub struct TemporaryPage { 10 | page: Page, 11 | allocator: TinyAllocator, 12 | } 13 | 14 | impl TemporaryPage { 15 | pub fn new(page: Page, allocator: TinyAllocator) -> TemporaryPage { 16 | TemporaryPage { 17 | page, 18 | allocator, 19 | } 20 | } 21 | 22 | /// Maps the temporary page to the given frame in the active table. 23 | /// Returns the start address of the temporary page. 24 | pub fn map(&mut self, frame: Frame, active_table: &mut PageMapper) -> VirtualAddress { 25 | use super::EntryFlags; 26 | use super::PageLike; 27 | 28 | assert!(active_table.translate(&self.page.start_address()).is_none(), "Temporary page is already mapped"); 29 | active_table.map_to(self.page.clone(), frame, EntryFlags::WRITABLE, &mut self.allocator); 30 | self.page.start_address() 31 | } 32 | 33 | /// Un maps the temporary page in the active table without freeing the frame 34 | pub fn discard(&mut self, active_table: &mut PageMapper) { 35 | active_table.discard(self.page.clone(), &mut self.allocator); 36 | } 37 | 38 | /// Maps the temporary page to the given page table frame in the active 39 | /// table. Returns a reference to the now mapped table. 40 | pub fn map_table_frame(&mut self, frame: Frame, active_table: &mut PageMapper) -> &mut PageTable { 41 | self.map(frame, active_table); 42 | unsafe { super::functions::as_table_root(self.page.clone()) } 43 | } 44 | 45 | pub fn unwrap(self) -> TinyAllocator { 46 | self.allocator 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kernel/src/shell/evaluator.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use alloc::BTreeMap; 3 | use alloc::String; 4 | use alloc::Vec; 5 | use super::Process; 6 | 7 | pub struct Evaluator { 8 | commands: BTreeMap, 9 | } 10 | 11 | impl Evaluator { 12 | pub fn new() -> Evaluator { 13 | Evaluator { 14 | commands: BTreeMap::new(), 15 | } 16 | } 17 | 18 | pub fn get_options(&self, prefix: &str) -> Vec { 19 | self.commands.keys().filter_map(|string| { 20 | if string.starts_with(prefix) { 21 | return Some(string.clone()); 22 | } 23 | None 24 | }).collect() 25 | } 26 | 27 | pub fn add_option(&mut self, command: &str, traversal: Traversal) { 28 | use alloc::string::ToString; 29 | self.commands.insert(command.to_string(), traversal); 30 | } 31 | 32 | pub fn traverse(&mut self, command: &String) -> Option<&mut Traversal> { 33 | self.commands.get_mut(command) 34 | } 35 | } 36 | 37 | pub enum Traversal { 38 | Process(Box), 39 | Evaluator(Evaluator), 40 | } -------------------------------------------------------------------------------- /kernel/src/shell/evaluators/memory.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Deref; 2 | use memory::*; 3 | use shell::ClosureProcess; 4 | use super::Evaluator; 5 | use super::Traversal; 6 | 7 | pub fn construct() -> Evaluator { 8 | let mut evaluator = Evaluator::new(); 9 | evaluator.add_option("available", available()); 10 | evaluator.add_option("test", super::memory_test::memory_test_process()); 11 | evaluator 12 | } 13 | 14 | fn available() -> Traversal { 15 | ClosureProcess::new_traversal(|| { 16 | let free_frames = FrameLikeAllocator::::free_frames_count(FRAME_ALLOCATOR.lock().deref()); 17 | let used_frames = FrameLikeAllocator::::used_frames_count(FRAME_ALLOCATOR.lock().deref()); 18 | let total_frames = free_frames + used_frames; 19 | println!("{} megabytes total memory ({} frames)", total_frames / 256, total_frames); 20 | println!("{} megabytes of free memory ({} frames)", free_frames / 256, free_frames); 21 | println!("{} megabytes of used memory ({} frames)", used_frames / 256, used_frames); 22 | }) 23 | } 24 | 25 | -------------------------------------------------------------------------------- /kernel/src/shell/evaluators/memory_test.rs: -------------------------------------------------------------------------------- 1 | use alloc::Vec; 2 | use core::ops::DerefMut; 3 | use memory::Frame; 4 | use memory::FRAME_ALLOCATOR; 5 | use memory::FrameLikeAllocator; 6 | use paging::Page; 7 | use paging::PageLike; 8 | 9 | /// Returns false if the given frame referenced by the page is erroneous 10 | fn is_good_frame(page: Page) -> bool { 11 | use utility::PseudoRandomGenerator; 12 | const MAX_INDEX: usize = (Page::SIZE as usize / 8) - 1; 13 | let seeded_generator = || PseudoRandomGenerator::new(page.start_address().raw() as u64); 14 | 15 | let mut generator = seeded_generator(); 16 | for index in 0..MAX_INDEX { 17 | let mut region = (page.start_address().raw() + index * 8) as *mut u64; 18 | unsafe { *region = generator.next() }; 19 | } 20 | 21 | let mut generator = seeded_generator(); 22 | for index in 0..MAX_INDEX { 23 | let mut region = (page.start_address().raw() + index * 8) as *mut u64; 24 | unsafe { 25 | if *region != generator.next() { 26 | return false; 27 | } 28 | } 29 | } 30 | return true; 31 | } 32 | 33 | fn allocate_frames(free_frames_count: usize) -> (u64, usize, Vec>) { 34 | use paging::ACTIVE_PAGE_TABLE; 35 | use paging::EntryFlags; 36 | let mut frames: Vec> = vec![None; free_frames_count]; 37 | 38 | let mut frame_allocator = FRAME_ALLOCATOR.lock(); 39 | let frame_allocator = frame_allocator.deref_mut(); 40 | let mut page_table = ACTIVE_PAGE_TABLE.lock(); 41 | let page_table = page_table.deref_mut(); 42 | 43 | let mut allocation_count = 0; 44 | let mut erroneous_frames_count = 0; 45 | let temporary_page = Page::from_address(::paging::reserved::TEMPORARY_PAGE); 46 | loop { 47 | let frame: Frame; 48 | if let Some(new_frame) = frame_allocator.allocate() { 49 | frame = new_frame; 50 | } else { 51 | break; 52 | } 53 | 54 | page_table.map_to(temporary_page.clone(), frame, EntryFlags::WRITABLE, frame_allocator); 55 | if is_good_frame(temporary_page.clone()) { 56 | print!("."); 57 | } else { 58 | erroneous_frames_count += 1; 59 | eprint!("x"); 60 | } 61 | let frame = page_table.un_map(temporary_page.clone(), frame_allocator); 62 | 63 | frames[allocation_count] = Some(frame); 64 | allocation_count += 1; 65 | if allocation_count % 64 == 0 { 66 | println!(" {} frames", allocation_count); 67 | } 68 | } 69 | 70 | (erroneous_frames_count, allocation_count, frames) 71 | } 72 | 73 | fn deallocate_frames(mut frames: Vec>) { 74 | for allocated_frame in frames.iter_mut() { 75 | let frame: Frame; 76 | if let Some(new_frame) = allocated_frame.take() { 77 | frame = new_frame; 78 | } else { 79 | return; 80 | } 81 | 82 | FRAME_ALLOCATOR.lock().deallocate(frame); 83 | } 84 | } 85 | 86 | fn memory_test() { 87 | use utility::math::percentage; 88 | use core::ops::Deref; 89 | use display::text_mode::LowDepthColour; 90 | use display::text_mode::Printer; 91 | 92 | let free_frames_count = FrameLikeAllocator::::free_frames_count(FRAME_ALLOCATOR.lock().deref()); 93 | let (erroneous_frames_count, allocation_count, frames) = allocate_frames(free_frames_count); 94 | deallocate_frames(frames); 95 | let bad_frames_percentage = percentage(erroneous_frames_count, allocation_count as u64); 96 | 97 | println!("\n"); 98 | println!("=================================="); 99 | println!("Projected free frames count: {}", free_frames_count); 100 | println!("Actual checked frames: {}", allocation_count); 101 | if erroneous_frames_count > 0 { 102 | eprintln!("Erroneous frames: {} ({}%)", erroneous_frames_count, bad_frames_percentage); 103 | } else { 104 | let mut printer = ::display::text_mode::SYSTEM_PRINTER.lock(); 105 | printer.print_coloured("Erroneous frames: 0 (0%)\n", &LowDepthColour::BACKGROUND, &LowDepthColour::LightGreen); 106 | } 107 | println!("=================================="); 108 | } 109 | 110 | pub fn memory_test_process() -> super::Traversal { 111 | use shell::ClosureProcess; 112 | ClosureProcess::new_traversal(|| memory_test()) 113 | } 114 | -------------------------------------------------------------------------------- /kernel/src/shell/evaluators/mod.rs: -------------------------------------------------------------------------------- 1 | use super::Evaluator; 2 | use super::Traversal; 3 | 4 | pub mod root; 5 | pub mod memory; 6 | pub mod memory_test; 7 | -------------------------------------------------------------------------------- /kernel/src/shell/evaluators/root.rs: -------------------------------------------------------------------------------- 1 | use super::Evaluator; 2 | use super::Traversal; 3 | 4 | pub fn construct() -> Evaluator { 5 | let mut evaluator = Evaluator::new(); 6 | evaluator.add_option("memory", Traversal::Evaluator(super::memory::construct())); 7 | evaluator 8 | } -------------------------------------------------------------------------------- /kernel/src/shell/functions.rs: -------------------------------------------------------------------------------- 1 | use super::KernelShell; 2 | use utility::Global; 3 | 4 | pub static SYSTEM_SHELL: Global = Global::new("SYSTEM_SHELL"); 5 | 6 | pub fn initialize() { 7 | let _status = ::display::text_mode::BootStatus::new("Initializing kernel shell"); 8 | SYSTEM_SHELL.set(KernelShell::new()); 9 | } 10 | -------------------------------------------------------------------------------- /kernel/src/shell/kernel_shell.rs: -------------------------------------------------------------------------------- 1 | use alloc::String; 2 | use alloc::Vec; 3 | use super::Evaluator; 4 | use super::ShellDisplay; 5 | use super::Traversal; 6 | 7 | pub struct KernelShell { 8 | display: ShellDisplay, 9 | initialized: bool, 10 | root: Evaluator, 11 | 12 | complete_options: Vec, 13 | path: Vec, 14 | control_key_held: bool, 15 | 16 | printed_count: usize, 17 | } 18 | 19 | impl KernelShell { 20 | const PROMPT: &'static str = "> "; 21 | 22 | pub fn new() -> KernelShell { 23 | KernelShell { 24 | display: ShellDisplay::new(), 25 | initialized: false, 26 | root: super::evaluators::root::construct(), 27 | complete_options: vec![], 28 | path: vec![], 29 | control_key_held: false, 30 | printed_count: 0, 31 | } 32 | } 33 | 34 | pub fn on_key_press(&mut self, key_event: ::keyboard::KeyEvent) { 35 | use keyboard::KeyState; 36 | use keyboard::KeyCode; 37 | use keyboard::key_other::KeySpecial; 38 | use keyboard::key_other::KeyModifier; 39 | 40 | let key_code = key_event.key_code; 41 | if key_event.state == KeyState::Released { 42 | if let KeyCode::Modifier(KeyModifier::ControlLeft) = key_code { 43 | self.control_key_held = false; 44 | } 45 | return; 46 | } 47 | 48 | self.try_initialize(); 49 | match key_code { 50 | KeyCode::Modifier(KeyModifier::ControlLeft) => self.control_key_held = true, 51 | KeyCode::Special(KeySpecial::Backspace) => self.pop_printable(), 52 | KeyCode::Special(KeySpecial::Tab) => self.complete_option(), 53 | KeyCode::Special(KeySpecial::Enter) => { 54 | if self.run_process() { 55 | self.initialized = false; 56 | } else { 57 | self.reset_prompt(); 58 | self.update_options(); 59 | } 60 | } 61 | KeyCode::Character(character) => { 62 | let character = character.to_char(); 63 | if !self.change_selection(character) { 64 | self.push_printable(character) 65 | } 66 | } 67 | KeyCode::Number(character) => self.push_printable(character.to_char()), 68 | KeyCode::Symbol(character) => self.push_printable(character.to_char()), 69 | _ => (), 70 | } 71 | } 72 | 73 | fn reset_prompt(&mut self) { 74 | self.display.print(Self::PROMPT); 75 | self.printed_count = Self::PROMPT.len(); 76 | self.path = vec![String::new()]; 77 | } 78 | 79 | fn change_selection(&mut self, character: char) -> bool { 80 | if !self.control_key_held { return false; } 81 | match character { 82 | 'j' => self.display.offset_selected(1), 83 | 'k' => self.display.offset_selected(-1), 84 | _ => () 85 | } 86 | self.update_options(); 87 | true 88 | } 89 | 90 | fn push_printable(&mut self, character: char) { 91 | if self.printed_count == self.display.main_width() { return; } 92 | self.printed_count += 1; 93 | 94 | self.display.push_character(character); 95 | if character == '.' { 96 | self.path.push(String::new()); 97 | } else { 98 | self.path.last_mut().unwrap().push(character); 99 | } 100 | self.update_options(); 101 | } 102 | 103 | fn pop_printable(&mut self) { 104 | if self.printed_count > Self::PROMPT.len() { 105 | self.printed_count -= 1; 106 | } 107 | 108 | if self.path.last().unwrap().len() == 0 { 109 | if self.path.len() > 1 { 110 | self.path.pop(); 111 | self.display.pop_character(); 112 | } 113 | } else { 114 | self.display.pop_character(); 115 | self.path.last_mut().unwrap().pop(); 116 | } 117 | self.update_options(); 118 | } 119 | 120 | fn update_options(&mut self) { 121 | let options = self.get_options(); 122 | self.display.set_options(&options); 123 | self.complete_options = options; 124 | } 125 | 126 | fn get_options(&mut self) -> Vec { 127 | Self::traverse(&mut self.root, &self.path) 128 | .and_then(|(evaluator, last)| Some(evaluator.get_options(last))) 129 | .unwrap_or(vec![]) 130 | } 131 | 132 | fn run_process(&mut self) -> bool { 133 | self.display.push_character('\n'); 134 | 135 | if let Some((evaluator, last)) = Self::traverse(&mut self.root, &self.path) { 136 | if let Some(traversal) = evaluator.traverse(last) { 137 | return match traversal { 138 | Traversal::Evaluator(_) => { 139 | self.display.print_error("Shell path is a module not a process\n"); 140 | false 141 | } 142 | Traversal::Process(process) => { 143 | process.run(); 144 | true 145 | } 146 | }; 147 | } 148 | } 149 | 150 | self.display.print_error("Unknown shell path to process\n"); 151 | false 152 | } 153 | 154 | fn traverse<'a>(current: &'a mut Evaluator, rest: &'a [String]) -> Option<(&'a mut Evaluator, &'a String)> { 155 | if rest.len() == 1 { 156 | return Some((current, &rest[0])); 157 | } 158 | 159 | let (next, rest) = rest.split_first()?; 160 | match current.traverse(&next)? { 161 | Traversal::Evaluator(ref mut evaluator) => Self::traverse(evaluator, rest), 162 | _ => None, 163 | } 164 | } 165 | 166 | fn complete_option(&mut self) { 167 | if self.complete_options.is_empty() { return; } 168 | 169 | let selected = self.display.get_selected(); 170 | { 171 | let option = &self.complete_options[selected]; 172 | let remaining = &option[self.path.last().unwrap().len()..]; 173 | 174 | if self.printed_count + remaining.len() > self.display.main_width() { return; } 175 | self.printed_count += remaining.len(); 176 | 177 | self.display.print(remaining); 178 | self.path.last_mut().unwrap().push_str(remaining); 179 | } 180 | 181 | self.update_options(); 182 | } 183 | 184 | fn try_initialize(&mut self) { 185 | if !self.initialized { 186 | self.display.redraw(); 187 | self.reset_prompt(); 188 | self.update_options(); 189 | self.initialized = true; 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /kernel/src/shell/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::evaluator::Evaluator; 2 | pub use self::evaluator::Traversal; 3 | pub use self::functions::SYSTEM_SHELL; 4 | pub use self::kernel_shell::KernelShell; 5 | pub use self::process::ClosureProcess; 6 | pub use self::process::Process; 7 | use self::shell_display::ShellDisplay; 8 | 9 | pub mod kernel_shell; 10 | pub mod shell_display; 11 | pub mod functions; 12 | pub mod evaluator; 13 | pub mod evaluators; 14 | pub mod process; 15 | -------------------------------------------------------------------------------- /kernel/src/shell/process.rs: -------------------------------------------------------------------------------- 1 | pub trait Process: Send { 2 | fn run(&mut self); 3 | } 4 | 5 | pub struct ClosureProcess { 6 | closure: F, 7 | } 8 | 9 | impl ClosureProcess where F: 'static + Send { 10 | pub fn new(closure: F) -> ClosureProcess { 11 | ClosureProcess { 12 | closure, 13 | } 14 | } 15 | 16 | pub fn new_traversal(closure: F) -> super::Traversal { 17 | super::Traversal::Process(box Self::new(closure)) 18 | } 19 | } 20 | 21 | impl Process for ClosureProcess where F: Send { 22 | fn run(&mut self) { 23 | (self.closure)(); 24 | } 25 | } -------------------------------------------------------------------------------- /kernel/src/shell/shell_display.rs: -------------------------------------------------------------------------------- 1 | use alloc::String; 2 | use display::text_mode::*; 3 | use display::text_mode::buffers::BootBuffer; 4 | use display::text_mode::buffers::WindowBuffer; 5 | use display::text_mode::drivers::GlobalFacade; 6 | use display::text_mode::printers::ScrollPrinter; 7 | use display::text_mode::printers::SelectableList; 8 | 9 | pub struct ShellDisplay { 10 | display: BootBuffer, 11 | main_panel: ScrollPrinter>, 12 | list_panel: SelectableList>, 13 | } 14 | 15 | impl ShellDisplay { 16 | const SIDEBAR_WIDTH: usize = 20; 17 | 18 | pub fn new() -> ShellDisplay { 19 | let display = BootBuffer::new(GlobalFacade); 20 | let main_panel = { 21 | let position = Position { column: 0, row: 0 }; 22 | let width = display.width() - Self::SIDEBAR_WIDTH; 23 | let height = display.height(); 24 | let buffer = WindowBuffer::new(GlobalFacade, position, width, height); 25 | ScrollPrinter::new(buffer) 26 | }; 27 | let mut list_panel = { 28 | let position = Position { column: display.width() - (Self::SIDEBAR_WIDTH - 1), row: 0 }; 29 | let width = Self::SIDEBAR_WIDTH - 1; 30 | let height = display.height(); 31 | let buffer = WindowBuffer::new(GlobalFacade, position, width, height); 32 | SelectableList::new(buffer) 33 | }; 34 | list_panel.set_selected(0); 35 | ShellDisplay { 36 | display, 37 | main_panel, 38 | list_panel, 39 | } 40 | } 41 | 42 | pub fn main_width(&self) -> usize { 43 | self.display.width() - Self::SIDEBAR_WIDTH 44 | } 45 | 46 | pub fn redraw(&mut self) { 47 | self.draw_interface(); 48 | self.list_panel.flush_all(); 49 | self.main_panel.flush_all(); 50 | } 51 | 52 | fn draw_interface(&mut self) { 53 | let separator_x = self.display.width() - Self::SIDEBAR_WIDTH; 54 | for y in 0..self.display.height() { 55 | self.display[separator_x][y].character = b'|'; 56 | } 57 | self.display.flush_all(); 58 | } 59 | 60 | pub fn print(&mut self, string: &str) { 61 | self.main_panel.print(string); 62 | } 63 | 64 | pub fn print_error(&mut self, string: &str) { 65 | self.main_panel.print_error(string); 66 | } 67 | 68 | pub fn push_character(&mut self, character: char) { 69 | use alloc::string::ToString; 70 | self.main_panel.print(&character.to_string()); 71 | self.list_panel.set_selected(0); 72 | } 73 | 74 | pub fn pop_character(&mut self) { 75 | self.main_panel.print("\x08 \x08"); 76 | self.list_panel.set_selected(0); 77 | } 78 | 79 | pub fn set_options(&mut self, options: &[String]) { 80 | self.list_panel.set_list(options); 81 | } 82 | 83 | pub fn offset_selected(&mut self, offset: i32) { 84 | self.list_panel.offset_selected(offset); 85 | } 86 | 87 | pub fn get_selected(&self) -> usize { 88 | self.list_panel.get_selected() 89 | } 90 | } -------------------------------------------------------------------------------- /kernel/src/structures/fixed_stack.rs: -------------------------------------------------------------------------------- 1 | use paging::Page; 2 | use paging::PageLike; 3 | 4 | type StackData = [u8; Page::SIZE as usize]; 5 | 6 | /// An uninitialized stack the size of one normal page 7 | pub struct FixedStack { 8 | stack: StackData, 9 | } 10 | 11 | impl FixedStack { 12 | pub const fn new() -> FixedStack { 13 | FixedStack { 14 | stack: [0; Page::SIZE as usize], 15 | } 16 | } 17 | 18 | pub fn address(&mut self) -> usize { 19 | // Stacks grow downward so it starts at the end of the 20 | // array instead of the start 21 | (&mut self.stack[Page::SIZE as usize - 1] as *mut _) as _ 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /kernel/src/structures/frame_store.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | use memory::Frame; 3 | use memory::FrameLike; 4 | use memory::FrameLikeAllocator; 5 | use paging::EntryFlags; 6 | use paging::Page; 7 | use paging::PageIter; 8 | use paging::PageLike; 9 | 10 | pub const MAX_FRAMES: usize = 255; 11 | 12 | // This structure has been created for the 13 | // express purpose of not using heap allocation 14 | // to store the frames. Using heap allocation 15 | // causes a deadlock due to the way it works in 16 | // the kernel. See memory/functions::handle_heap_fault 17 | pub struct FrameStore { 18 | pages: PageIter, 19 | top: NonNull>, 20 | size: usize, 21 | } 22 | 23 | impl FrameStore { 24 | pub fn new(mut pages: PageIter, allocator: &mut FrameLikeAllocator) -> FrameStore { 25 | let root = Self::construct_node(pages.next().expect("Out of pages"), allocator); 26 | FrameStore { 27 | pages, 28 | top: root, 29 | size: 0, 30 | } 31 | } 32 | 33 | pub fn push(&mut self, frame: F, allocator: &mut FrameLikeAllocator) { 34 | let index = self.size % MAX_FRAMES; 35 | if index == 0 && self.size != 0 { 36 | if unsafe { self.top.as_mut() }.next.is_none() { 37 | let page = self.pages.next().expect("Out of pages"); 38 | let mut node = Self::construct_node(page, allocator); 39 | unsafe { 40 | node.as_mut().previous = Some(self.top); 41 | self.top.as_mut().next = Some(node); 42 | } 43 | } 44 | self.top = unsafe { self.top.as_mut() }.next.unwrap(); 45 | } 46 | unsafe { self.top.as_mut() }.frames[index] = Some(frame); 47 | self.size += 1; 48 | } 49 | 50 | pub fn pop(&mut self) -> Option { 51 | if self.size == 0 { 52 | return None; 53 | } 54 | 55 | self.size -= 1; 56 | let index = self.size % MAX_FRAMES; 57 | let frame = unsafe { self.top.as_mut() }.frames[index].take().unwrap(); 58 | 59 | if self.size != 0 && index == 0 { 60 | self.top = unsafe { self.top.as_mut() }.previous.unwrap(); 61 | } 62 | Some(frame) 63 | } 64 | 65 | pub fn size(&self) -> usize { 66 | self.size 67 | } 68 | 69 | fn construct_node(page: Page, allocator: &mut FrameLikeAllocator) -> NonNull> { 70 | let frame = allocator.allocate().expect("Out of memory: FrameStore"); 71 | let mut table = ::paging::ACTIVE_PAGE_TABLE.lock(); 72 | table.map_to(page.clone(), frame, EntryFlags::WRITABLE, allocator); 73 | 74 | let node = page.start_address().raw() as *mut FrameStoreNode; 75 | let node = NonNull::new(node).unwrap(); 76 | unsafe { ::core::ptr::write(node.as_ptr(), FrameStoreNode::new()); } 77 | node 78 | } 79 | } 80 | 81 | unsafe impl Send for FrameStore {} 82 | 83 | pub struct FrameStoreNode { 84 | pub previous: Option>>, 85 | pub next: Option>>, 86 | pub frames: [Option; MAX_FRAMES], 87 | } 88 | 89 | impl FrameStoreNode { 90 | pub fn new() -> FrameStoreNode { 91 | FrameStoreNode { 92 | previous: None, 93 | next: None, 94 | frames: unsafe { ::core::mem::uninitialized() }, 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /kernel/src/structures/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::fixed_stack::FixedStack; 2 | pub use self::frame_store::FrameStore; 3 | 4 | pub mod fixed_stack; 5 | pub mod frame_store; -------------------------------------------------------------------------------- /kernel/src/system_call/functions.rs: -------------------------------------------------------------------------------- 1 | // Change this code to your desire! 2 | pub fn system_call_hook(code: u64) { 3 | if code == 0xdeadbeef { 4 | println!("First"); 5 | } else { 6 | eprintln!("Second {:#x}", code) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /kernel/src/system_call/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod functions; 2 | -------------------------------------------------------------------------------- /kernel/src/task/functions.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use paging::VirtualAddress; 3 | use super::Scheduler; 4 | use super::Thread; 5 | use utility::Global; 6 | 7 | pub static ACTIVE_THREAD: Global = Global::new("ACTIVE_THREAD"); 8 | pub static SCHEDULER: Global> = Global::new("SCHEDULER"); 9 | 10 | pub fn initialize() { 11 | let _status = ::display::text_mode::BootStatus::new("Creating preemptive scheduler"); 12 | enable_cpu_features(); 13 | 14 | let scheduler = super::schedulers::RoundRobin::new(); 15 | SCHEDULER.set(box scheduler); 16 | } 17 | 18 | pub fn pre_initialize() { 19 | use paging::Page; 20 | use paging::PageLike; 21 | use memory::FrameLikeAllocator; 22 | use core::ops::DerefMut; 23 | 24 | let _status = ::display::text_mode::BootStatus::new("Allocating context switch stacks"); 25 | let mut active_table = ::paging::ACTIVE_PAGE_TABLE.lock(); 26 | let mut allocator = ::memory::FRAME_ALLOCATOR.lock(); 27 | 28 | // We use a separate stack when facilitating a context switch 29 | // See interrupts/handlers::timer_handler 30 | let stack_start = Page::from_address(::paging::reserved::TASK_SWITCH_STACK_BOTTOM); 31 | let stack_end = Page::from_address(::paging::reserved::TASK_SWITCH_STACK_TOP); 32 | let pages = ::paging::PageIter::inclusive(stack_start, stack_end); 33 | for page in pages { 34 | let frame = allocator.allocate().expect("Out of memory: CONTEXT_SWITCH_STACK_ALLOCATION"); 35 | active_table.map_to(page, frame, ::paging::EntryFlags::WRITABLE, allocator.deref_mut()); 36 | } 37 | } 38 | 39 | fn enable_cpu_features() { 40 | // This enables usage of the "sysenter" instruction 41 | // However, system calls in this kernel currently 42 | // utilize the int 0xaa instruction instead 43 | 44 | use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr}; 45 | const SCE_BIT: u64 = 1; 46 | unsafe { 47 | let efer = rdmsr(IA32_EFER); 48 | wrmsr(IA32_EFER, efer | SCE_BIT); 49 | } 50 | } 51 | 52 | pub extern "C" fn context_switch(stack_pointer: usize) -> usize { 53 | use core::ops::DerefMut; 54 | use paging::PageLike; 55 | 56 | let mut scheduler = SCHEDULER.lock(); 57 | let mut active_thread = ACTIVE_THREAD.lock_direct(); 58 | let mut active_table = ::paging::ACTIVE_PAGE_TABLE.lock(); 59 | 60 | // If this is our first context_switch, then there won't be 61 | // an active thread 62 | if let Some(mut thread) = active_thread.take() { 63 | thread.stack_pointer = VirtualAddress::new(stack_pointer); 64 | scheduler.schedule_new(thread); 65 | } 66 | 67 | let new_thread: Thread = scheduler.schedule_next().expect("Scheduler is empty"); 68 | ::core::mem::replace(active_thread.deref_mut(), Some(new_thread.clone())); 69 | 70 | // This stack is switched to whenever an interrupt occurs in user mode 71 | // A kernel stack is needed to facilitate system calls and timer interrupts 72 | let kernel_stack = ::x86_64::VirtualAddress(new_thread.kernel_stack.end_address().raw()); 73 | ::interrupts::functions::TSS.lock().privilege_stack_table[0] = kernel_stack; 74 | 75 | // Switching page tables invalidates the previous kernel stack so 76 | // that's why we use a separate stack for handling the context switch 77 | active_table.switch(new_thread.page_table); 78 | 79 | ::interrupts::send_interrupt_end(false); 80 | new_thread.stack_pointer.raw() 81 | } 82 | -------------------------------------------------------------------------------- /kernel/src/task/loaders/flat_binary.rs: -------------------------------------------------------------------------------- 1 | use paging::InactivePageTable; 2 | use paging::VirtualAddress; 3 | use task::Thread; 4 | 5 | pub fn load_flat_binary(binary: &[u8], mut base_table: InactivePageTable, entry_point: VirtualAddress) -> Thread { 6 | use super::functions; 7 | use paging::EntryFlags; 8 | 9 | // First we copy the binary into memory and map it at the start 10 | // of the virtual address space 11 | // Warning: Keep in mind that executing code at address 0 will 12 | // cause a General Protection Fault so do not set your 13 | // entry point to be at address 0 (null) 14 | functions::map_data(binary, &mut base_table, VirtualAddress::new(0), 15 | EntryFlags::USER_ACCESSIBLE); 16 | 17 | // We create stacks at intervals of sixteen pages, so here 18 | // we calculate the next stack location 19 | let last_address = binary.len() - 1; 20 | let stack_bottom = ::utility::math::align_up_usize(last_address, super::stack::STACK_SIZE as usize); 21 | let stack_bottom = VirtualAddress::new(stack_bottom); 22 | let (kernel_stack, stack_pointer) = super::stack::create_local_stack(stack_bottom, &mut base_table); 23 | 24 | let stack_data = super::stack::create_initial_stack(&entry_point, &stack_pointer); 25 | functions::write_data(::utility::convert::as_u8_slice(&stack_data), &mut base_table, stack_pointer.clone()); 26 | 27 | Thread { 28 | page_table: base_table, 29 | kernel_stack, 30 | stack_pointer, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kernel/src/task/loaders/functions.rs: -------------------------------------------------------------------------------- 1 | use core::ops::DerefMut; 2 | use memory::Frame; 3 | use memory::FRAME_ALLOCATOR; 4 | use memory::FrameLike; 5 | use memory::FrameLikeAllocator; 6 | use paging::ACTIVE_PAGE_TABLE; 7 | use paging::ActivePageTable; 8 | use paging::EntryFlags; 9 | use paging::InactivePageTable; 10 | use paging::Page; 11 | use paging::PageIter; 12 | use paging::PageLike; 13 | use paging::VirtualAddress; 14 | 15 | pub fn map_data(data: &[u8], table: &mut InactivePageTable, offset: VirtualAddress, flags: EntryFlags) { 16 | let end_address = offset.offset(data.len() - 1); 17 | let start_page = Page::from_address(offset.clone()); 18 | let end_page = Page::from_address(end_address); 19 | allocate_region(PageIter::inclusive(start_page, end_page), table, flags); 20 | write_data(data, table, offset); 21 | } 22 | 23 | /// Maps a region of the inactive page table's virtual address space 24 | /// into physical memory 25 | pub fn allocate_region(region: PageIter, table: &mut InactivePageTable, flags: EntryFlags) { 26 | let mut allocator = FRAME_ALLOCATOR.lock(); 27 | let allocator = allocator.deref_mut(); 28 | for page in region { 29 | let frame = allocator.allocate().expect("Out of memory: REGION_ALLOCATION"); 30 | ACTIVE_PAGE_TABLE.lock().with(table, allocator, |table, allocator| { 31 | table.map_to(page, frame, flags.clone(), allocator); 32 | }); 33 | } 34 | } 35 | 36 | /// Writes an array of bytes at a virtual address in the inactive table's 37 | /// virtual address space 38 | pub fn write_data(data: &[u8], table: &mut InactivePageTable, offset: VirtualAddress) { 39 | let staging_page = Page::from_address(::paging::reserved::TEMPORARY_PAGE); 40 | let mut active_table = ACTIVE_PAGE_TABLE.lock(); 41 | let mut allocator = FRAME_ALLOCATOR.lock(); 42 | let allocator = allocator.deref_mut(); 43 | 44 | let mut map_frame = |active_table: &mut ActivePageTable, allocator: &mut FrameLikeAllocator, 45 | address: &VirtualAddress| { 46 | let frame = active_table.with(table, allocator, |table, _| { 47 | Frame::from_address(table.translate(address).expect("Data destination not mapped")) 48 | }); 49 | active_table.map_to(staging_page.clone(), frame, EntryFlags::WRITABLE, allocator); 50 | }; 51 | 52 | map_frame(&mut active_table, allocator, &offset); 53 | for (index, byte) in data.iter().enumerate() { 54 | let destination_address = offset.offset(index); 55 | let on_page_boundary = destination_address.raw() as u64 % Page::SIZE == 0; 56 | if on_page_boundary && index != 0 { 57 | active_table.discard(staging_page.clone(), allocator); 58 | map_frame(&mut active_table, allocator, &destination_address); 59 | } 60 | 61 | let byte_index = (destination_address.raw() as u64 % Page::SIZE) as usize; 62 | let destination_byte = (staging_page.start_address().raw() + byte_index) as *mut u8; 63 | unsafe { *destination_byte = *byte; } 64 | } 65 | active_table.discard(staging_page.clone(), allocator); 66 | } 67 | -------------------------------------------------------------------------------- /kernel/src/task/loaders/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flat_binary; 2 | pub mod functions; 3 | pub mod stack; -------------------------------------------------------------------------------- /kernel/src/task/loaders/stack.rs: -------------------------------------------------------------------------------- 1 | use paging::InactivePageTable; 2 | use paging::Page; 3 | use paging::PageLike; 4 | use paging::VirtualAddress; 5 | 6 | // The compiler adds different numbers to the rsp 7 | // depending on the build mode 8 | #[cfg(debug_assertions)] 9 | pub const PADDING_COUNT: usize = 16; 10 | #[cfg(not(debug_assertions))] 11 | pub const PADDING_COUNT: usize = 15; 12 | 13 | pub const EXCEPTION_FRAME_SIZE: usize = 5; 14 | pub const INITIAL_STACK_SIZE: usize = EXCEPTION_FRAME_SIZE + PADDING_COUNT; 15 | 16 | pub const STACK_SIZE: u64 = 16 * Page::SIZE; 17 | 18 | pub fn create_initial_stack(entry_point: &VirtualAddress, stack_pointer: &VirtualAddress) 19 | -> [u64; INITIAL_STACK_SIZE] { 20 | // Here we setup a fake stack. When the scheduler gets to this 21 | // thread stack, it will pop all the registers and then iret. 22 | // We pretend that the thread just got interrupted and then 23 | // when the processor irets we will be placed into user mode 24 | // See the intel documentation on the exception stack frame 25 | 26 | // Interrupt Enable Flag, Reserved 27 | const R_FLAGS: u64 = 0b10_0000_0010; 28 | let mut stack = [0; INITIAL_STACK_SIZE]; 29 | stack[PADDING_COUNT] = entry_point.raw() as u64; 30 | stack[PADDING_COUNT + 1] = *::interrupts::functions::USER_CODE_SELECTOR.try().unwrap() as u64; 31 | stack[PADDING_COUNT + 2] = R_FLAGS; 32 | stack[PADDING_COUNT + 3] = stack_pointer.raw() as u64; 33 | stack[PADDING_COUNT + 4] = *::interrupts::functions::USER_DATA_SELECTOR.try().unwrap() as u64; 34 | stack 35 | } 36 | 37 | pub fn create_local_stack(stack_bottom: VirtualAddress, table: &mut InactivePageTable) 38 | -> (Page, VirtualAddress) { 39 | use paging::PageIter; 40 | use paging::EntryFlags; 41 | assert_eq!(stack_bottom.raw() as u64 % STACK_SIZE, 0); 42 | 43 | // Every thread needs a kernel stack, for use during 44 | // interrupt handling 45 | let kernel_stack_top = stack_bottom.offset(STACK_SIZE as usize - 1); 46 | let kernel_stack_page = Page::from_address(kernel_stack_top.clone()); 47 | super::functions::allocate_region(PageIter::inclusive(kernel_stack_page.clone(), kernel_stack_page.clone()), 48 | table, EntryFlags::WRITABLE); 49 | 50 | let stack_top = VirtualAddress::new(kernel_stack_top.raw() - Page::SIZE as usize); 51 | let stack_bottom_page = Page::from_address(stack_bottom); 52 | let stack_top_page = Page::from_address(stack_top.clone()); 53 | super::functions::allocate_region(PageIter::inclusive(stack_bottom_page, stack_top_page), 54 | table, EntryFlags::USER_ACCESSIBLE | EntryFlags::WRITABLE); 55 | (kernel_stack_page, stack_top) 56 | } -------------------------------------------------------------------------------- /kernel/src/task/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::functions::SCHEDULER; 2 | pub use self::scheduler::Scheduler; 3 | pub use self::thread::Thread; 4 | 5 | pub mod scheduler; 6 | pub mod schedulers; 7 | pub mod thread; 8 | pub mod functions; 9 | pub mod loaders; 10 | 11 | // loaders/flat_binary contains a function to load a "flat_binary" 12 | // Flat binaries only contain machine code and nothing else 13 | // You can generate one with NASM: 14 | // nasm -f bin 15 | // You can then load the file via the boot disk 16 | -------------------------------------------------------------------------------- /kernel/src/task/scheduler.rs: -------------------------------------------------------------------------------- 1 | use super::Thread; 2 | 3 | pub trait Scheduler { 4 | /// Halts the interrupted thread and selects the next thread to run 5 | fn schedule_next(&mut self) -> Option; 6 | 7 | /// Adds a new thread to a pool of threads to be executed 8 | fn schedule_new(&mut self, new_thread: Thread); 9 | } 10 | -------------------------------------------------------------------------------- /kernel/src/task/schedulers/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::round_robin::RoundRobin; 2 | use super::Scheduler; 3 | use super::Thread; 4 | 5 | pub mod round_robin; 6 | -------------------------------------------------------------------------------- /kernel/src/task/schedulers/round_robin.rs: -------------------------------------------------------------------------------- 1 | use alloc::VecDeque; 2 | use super::Thread; 3 | 4 | // The simplest scheduler possible 5 | // The next thread is the thread pushed on earliest 6 | // New threads are pushed to the back of the queue 7 | 8 | pub struct RoundRobin { 9 | threads: VecDeque, 10 | } 11 | 12 | impl RoundRobin { 13 | pub fn new() -> RoundRobin { 14 | RoundRobin { 15 | threads: VecDeque::new(), 16 | } 17 | } 18 | } 19 | 20 | impl super::Scheduler for RoundRobin { 21 | fn schedule_next(&mut self) -> Option { 22 | self.threads.pop_front() 23 | } 24 | 25 | fn schedule_new(&mut self, new_thread: Thread) { 26 | self.threads.push_back(new_thread); 27 | } 28 | } -------------------------------------------------------------------------------- /kernel/src/task/thread.rs: -------------------------------------------------------------------------------- 1 | use paging::InactivePageTable; 2 | use paging::Page; 3 | use paging::VirtualAddress; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Thread { 7 | pub page_table: InactivePageTable, 8 | pub kernel_stack: Page, 9 | pub stack_pointer: VirtualAddress, 10 | } 11 | 12 | impl Thread {} -------------------------------------------------------------------------------- /kernel/src/tests/display/mod.rs: -------------------------------------------------------------------------------- 1 | mod text_mode; -------------------------------------------------------------------------------- /kernel/src/tests/display/text_mode/buffer.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_fixed_buffer() { 3 | use display::text_mode::*; 4 | use display::text_mode::buffers::BootBuffer; 5 | 6 | let display = ::spin::Mutex::new(super::DummyDisplay::new()); 7 | let facade = super::DisplayFacade::new(&display); 8 | let mut fixed_buffer = BootBuffer::new(facade); 9 | 10 | { 11 | let position = Position { column: 79, row: 24 }; 12 | fixed_buffer.set_cell(&position, b'A'); 13 | fixed_buffer.flush_cell(&position); 14 | assert_eq!(display.lock().cell(&position).character, b'A'); 15 | 16 | fixed_buffer.set_cell(&position, b'B'); 17 | assert_ne!(display.lock().cell(&position).character, b'B'); 18 | } 19 | 20 | { 21 | let position = Position { column: 0, row: 0 }; 22 | fixed_buffer.set_foreground(&position, &LowDepthColour::Green); 23 | fixed_buffer.flush_cell(&position); 24 | assert_eq!(display.lock().cell(&position).foreground, LowDepthColour::Green); 25 | } 26 | 27 | { 28 | let position = Position { column: 50, row: 10 }; 29 | fixed_buffer.set_background(&position, &LowDepthColour::Red); 30 | fixed_buffer.flush_cell(&position); 31 | assert_eq!(display.lock().cell(&position).background, LowDepthColour::Red); 32 | } 33 | 34 | { 35 | let position = Position { column: 25, row: 14 }; 36 | fixed_buffer.set_cursor(&position); 37 | fixed_buffer.flush_cursor(); 38 | assert_eq!(display.lock().cursor, position); 39 | } 40 | } -------------------------------------------------------------------------------- /kernel/src/tests/display/text_mode/display_facade.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | use spin::Mutex; 3 | 4 | pub struct DisplayFacade<'a, D: 'a> { 5 | display: &'a Mutex, 6 | } 7 | 8 | impl<'a, D: TextDisplay> DisplayFacade<'a, D> { 9 | pub fn new(display: &'a Mutex) -> DisplayFacade<'a, D> { 10 | DisplayFacade { 11 | display, 12 | } 13 | } 14 | } 15 | 16 | impl<'a, D: TextDisplay> TextDisplay for DisplayFacade<'a, D> { 17 | fn width(&self) -> Width { 18 | self.display.lock().width() 19 | } 20 | 21 | fn height(&self) -> Height { 22 | self.display.lock().height() 23 | } 24 | 25 | fn set_cell(&mut self, position: &Position, character: Character) { 26 | self.display.lock().set_cell(position, character) 27 | } 28 | 29 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour) { 30 | self.display.lock().set_background(position, colour) 31 | } 32 | 33 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour) { 34 | self.display.lock().set_foreground(position, colour) 35 | } 36 | 37 | fn set_cursor(&mut self, position: &Position) { 38 | self.display.lock().set_cursor(position) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kernel/src/tests/display/text_mode/dummy_display.rs: -------------------------------------------------------------------------------- 1 | use display::text_mode::*; 2 | 3 | const HEIGHT: usize = 25; 4 | const WIDTH: usize = 80; 5 | 6 | type Buffer = [[TextCell; WIDTH]; HEIGHT]; 7 | 8 | pub struct DummyDisplay { 9 | pub buffer: Buffer, 10 | pub cursor: Position, 11 | } 12 | 13 | impl DummyDisplay { 14 | pub fn new() -> DummyDisplay { 15 | DummyDisplay { 16 | buffer: [[TextCell::default(); WIDTH]; HEIGHT], 17 | cursor: Position { column: 0, row: 0 }, 18 | } 19 | } 20 | 21 | pub fn cell(&self, position: &Position) -> &TextCell { 22 | &self.buffer[position.row][position.column] 23 | } 24 | } 25 | 26 | impl TextDisplay for DummyDisplay { 27 | fn width(&self) -> Width { 28 | WIDTH 29 | } 30 | 31 | fn height(&self) -> Height { 32 | HEIGHT 33 | } 34 | 35 | fn set_cell(&mut self, position: &Position, character: Character) { 36 | self.buffer[position.row][position.column].character = character; 37 | } 38 | 39 | fn set_background(&mut self, position: &Position, colour: &LowDepthColour) { 40 | self.buffer[position.row][position.column].background = *colour; 41 | } 42 | 43 | fn set_foreground(&mut self, position: &Position, colour: &LowDepthColour) { 44 | self.buffer[position.row][position.column].foreground = *colour; 45 | } 46 | 47 | fn set_cursor(&mut self, position: &Position) { 48 | self.cursor = position.clone(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /kernel/src/tests/display/text_mode/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::display_facade::DisplayFacade; 2 | pub use self::dummy_display::DummyDisplay; 3 | 4 | pub mod dummy_display; 5 | pub mod display_facade; 6 | mod buffer; 7 | mod scroll_printer; -------------------------------------------------------------------------------- /kernel/src/tests/display/text_mode/scroll_printer.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_scroll_printer() { 3 | use display::text_mode::*; 4 | use display::text_mode::printers::scroll_printer::ScrollPrinter; 5 | use display::text_mode::buffers::BootBuffer; 6 | 7 | let display = ::spin::Mutex::new(super::DummyDisplay::new()); 8 | let facade = super::DisplayFacade::new(&display); 9 | let buffer = BootBuffer::new(facade); 10 | let mut printer = ScrollPrinter::new(buffer); 11 | 12 | { 13 | let string = "This is a test"; 14 | printer.print(string); 15 | printer.print("\n"); 16 | let display = display.lock(); 17 | for (column, character) in string.bytes().enumerate() { 18 | let position = Position { 19 | column, 20 | row: 23, 21 | }; 22 | assert_eq!(display.cell(&position).character, character); 23 | } 24 | } 25 | 26 | { 27 | let string = "The second line?"; 28 | printer.print(string); 29 | let display = display.lock(); 30 | for (column, character) in string.bytes().enumerate() { 31 | let position = Position { 32 | column, 33 | row: 24, 34 | }; 35 | assert_eq!(display.cell(&position).character, character); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /kernel/src/tests/memory/fixed_frame_recycler.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_frames_can_fit_inside_frame_store() { 3 | let max_frames = ::memory::generic_allocators::fixed_frame_recycler::MAX_FRAMES; 4 | let max_store_frames = ::structures::frame_store::MAX_FRAMES; 5 | assert!(max_frames <= max_store_frames); 6 | } -------------------------------------------------------------------------------- /kernel/src/tests/memory/memory_area.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_memory_area() { 3 | use memory::MemoryArea; 4 | use memory::PhysicalAddress; 5 | 6 | { 7 | let first = MemoryArea::new(PhysicalAddress::new(100), 100); 8 | let other = MemoryArea::new(PhysicalAddress::new(150), 100); 9 | 10 | let overlap = first.overlap(&other).unwrap(); 11 | assert_eq!(overlap.start_address().raw(), 150); 12 | assert_eq!(overlap.end_address().raw(), 200); 13 | assert_eq!(overlap.size(), 50); 14 | } 15 | } -------------------------------------------------------------------------------- /kernel/src/tests/memory/mod.rs: -------------------------------------------------------------------------------- 1 | mod memory_area; 2 | mod fixed_frame_recycler; -------------------------------------------------------------------------------- /kernel/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod display; 2 | mod structures; 3 | mod memory; 4 | mod utility; -------------------------------------------------------------------------------- /kernel/src/tests/structures/frame_store.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_valid_size() { 3 | use core::mem::size_of; 4 | use structures::frame_store::FrameStoreNode; 5 | assert!(size_of::>() as u64 <= ::memory::Frame::SIZE); 6 | assert!(size_of::>() as u64 <= ::memory::Frame::SIZE); 7 | } 8 | -------------------------------------------------------------------------------- /kernel/src/tests/structures/mod.rs: -------------------------------------------------------------------------------- 1 | mod frame_store; -------------------------------------------------------------------------------- /kernel/src/tests/utility/math.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_align_up() { 3 | use utility::math::align_up_u64; 4 | assert_eq!(align_up_u64(0b1011_1111, 0b0100_0000), 0b1100_0000); 5 | assert_eq!(align_up_u64(0b0110_1011, 0b0100_0000), 0b1000_0000); 6 | } 7 | 8 | #[test] 9 | fn test_align_down() { 10 | use utility::math::align_down_u64; 11 | assert_eq!(align_down_u64(0b1011_1111, 0b0100_0000), 0b1000_0000); 12 | assert_eq!(align_down_u64(0b0110_1011, 0b0100_0000), 0b0100_0000); 13 | } 14 | -------------------------------------------------------------------------------- /kernel/src/tests/utility/mod.rs: -------------------------------------------------------------------------------- 1 | mod math; 2 | -------------------------------------------------------------------------------- /kernel/src/utility/convert.rs: -------------------------------------------------------------------------------- 1 | pub fn as_u8_slice(slice: &[T]) -> &[u8] { 2 | unsafe { 3 | ::core::slice::from_raw_parts(slice.as_ptr() as *const u8, 4 | slice.len() * ::core::mem::size_of::()) 5 | } 6 | } -------------------------------------------------------------------------------- /kernel/src/utility/global.rs: -------------------------------------------------------------------------------- 1 | use spin::Mutex; 2 | use spin::MutexGuard; 3 | 4 | pub struct Global { 5 | identifier: &'static str, 6 | object: Mutex>, 7 | } 8 | 9 | impl Global { 10 | pub const fn new(identifier: &'static str) -> Global { 11 | Global { 12 | identifier, 13 | object: Mutex::new(None), 14 | } 15 | } 16 | 17 | pub fn set(&self, object: T) { 18 | use core::ops::DerefMut; 19 | use core::mem::replace; 20 | replace(self.object.lock().deref_mut(), Some(object)); 21 | } 22 | 23 | pub fn lock_direct(&self) -> MutexGuard> { 24 | self.object.lock() 25 | } 26 | 27 | #[cfg(not(debug_assertions))] 28 | pub fn lock(&self) -> GlobalGuard { 29 | let guard = self.object.lock(); 30 | if guard.is_none() { panic!("Global not initialized: {}", self.identifier); } 31 | GlobalGuard { 32 | guard, 33 | } 34 | } 35 | 36 | #[cfg(debug_assertions)] 37 | pub fn lock(&self) -> GlobalGuard { 38 | GlobalGuard { 39 | // Our kernel runs on one CPU so any contention results 40 | // in a dead lock (until the scheduler has been created) 41 | guard: match self.object.try_lock() { 42 | Some(guard) => { 43 | if guard.is_none() { panic!("Global not initialized: {}", self.identifier); } 44 | guard 45 | } 46 | None => { 47 | eprintln!("Global object in contention: {}", self.identifier); 48 | self.object.lock() 49 | } 50 | }, 51 | } 52 | } 53 | } 54 | 55 | pub struct GlobalGuard<'a, T: 'a> { 56 | guard: MutexGuard<'a, Option>, 57 | } 58 | 59 | impl<'a, T> ::core::ops::Deref for GlobalGuard<'a, T> { 60 | type Target = T; 61 | 62 | fn deref(&self) -> &::Target { 63 | self.guard.deref().as_ref().unwrap() 64 | } 65 | } 66 | 67 | impl<'a, T> ::core::ops::DerefMut for GlobalGuard<'a, T> { 68 | fn deref_mut(&mut self) -> &mut ::Target { 69 | self.guard.deref_mut().as_mut().unwrap() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /kernel/src/utility/math.rs: -------------------------------------------------------------------------------- 1 | pub fn align_up_u64(number: u64, multiple: u64) -> u64 { 2 | align_down_u64(number + multiple - 1, multiple) 3 | } 4 | 5 | pub fn align_down_u64(number: u64, multiple: u64) -> u64 { 6 | assert!(multiple.is_power_of_two()); 7 | number & !(multiple - 1) 8 | } 9 | 10 | pub fn align_up_usize(number: usize, multiple: usize) -> usize { 11 | align_down_usize(number + multiple - 1, multiple) 12 | } 13 | 14 | pub fn align_down_usize(number: usize, multiple: usize) -> usize { 15 | assert!(multiple.is_power_of_two()); 16 | number & !(multiple - 1) 17 | } 18 | 19 | pub fn percentage(numerator: u64, denominator: u64) -> u64 { 20 | if denominator == 0 { 21 | return 0; 22 | } 23 | (100 * numerator + denominator / 2) / denominator 24 | } 25 | -------------------------------------------------------------------------------- /kernel/src/utility/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::global::Global; 2 | pub use self::multiboot_structure::MultibootStructure; 3 | pub use self::pseudo_random::PseudoRandomGenerator; 4 | 5 | pub mod math; 6 | pub mod convert; 7 | pub mod global; 8 | pub mod multiboot_structure; 9 | pub mod pseudo_random; 10 | -------------------------------------------------------------------------------- /kernel/src/utility/multiboot_structure.rs: -------------------------------------------------------------------------------- 1 | use multiboot2::BootInformation; 2 | 3 | // This structure is needed as multiboot2's ModuleIter cannot be cloned. 4 | // See memory/huge_frame_allocators/huge_boot_bump_allocator for its 5 | // main usage 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct MultibootStructure { 9 | address: usize, 10 | } 11 | 12 | impl MultibootStructure { 13 | pub fn new(address: usize) -> MultibootStructure { 14 | MultibootStructure { 15 | address: address + ::KERNEL_BASE as usize, 16 | } 17 | } 18 | 19 | pub fn get(&self) -> BootInformation { 20 | unsafe { ::multiboot2::load(self.address) } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /kernel/src/utility/pseudo_random.rs: -------------------------------------------------------------------------------- 1 | // Only use of this is in the shell's memory tester 2 | pub struct PseudoRandomGenerator { 3 | next: u64, 4 | } 5 | 6 | impl PseudoRandomGenerator { 7 | pub fn new(seed: u64) -> PseudoRandomGenerator { 8 | PseudoRandomGenerator { 9 | next: seed, 10 | } 11 | } 12 | 13 | pub fn next(&mut self) -> u64 { 14 | self.next = self.next.wrapping_mul(1103515245) + 12345; 15 | (self.next / 65536) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /kernel/x86_64-example_os.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "linker-flavor": "ld", 5 | "target-endian": "little", 6 | "target-pointer-width": "64", 7 | "target-c-int-width": "32", 8 | "executables": true, 9 | "arch": "x86_64", 10 | "os": "example", 11 | "panic-strategy": "abort", 12 | "disable-redzone": true, 13 | "features": "-mmx,-sse,+soft-float" 14 | } --------------------------------------------------------------------------------