diff --git a/.gitignore b/.gitignore index 8631604..3bbba9e 100755 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,9 @@ build/ target/ **/*.rs.bk Cargo.lock +**/Cargo.lock .DS_Store **/.DS_Store virtual.log -bc_flags \ No newline at end of file +bc_flags +prefix/ \ No newline at end of file diff --git a/Makefile b/Makefile index fb96402..7cffefa 100755 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ arch ?= x86_64 target ?= $(arch)-snowflake -boot2snow := build/arch/$(arch)/boot2snow/bootx64.efi +boot2snow := build/boot2snow/bootx64.efi kernel := build/kernel/kernel.bin img := build/snowflake-$(arch).img @@ -19,15 +19,15 @@ clean: @rm -r build #target run: $(img) - @qemu-system-x86_64 -bios ovmf.fd -m 2048 $(img) + @qemu-system-x86_64 -m 1024 -serial mon:stdio -net none -vga std -bios ovmf.fd $(img) run-debug: $(img) - @qemu-system-x86_64 -s -S -bios ovmf.fd $(img) + @qemu-system-x86_64 -s -S -m 1024 -serial mon:stdio -net none -vga std -bios ovmf.fd $(img) img: $(img) $(img): - @make -C arch/$(arch)/boot2snow + @make -C boot2snow @make -C kernel @dd if=/dev/zero of=$(img).tmp bs=512 count=98304 @mkfs.vfat $(img).tmp diff --git a/README.md b/README.md index 4e985cc..d3992d8 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # ![SnowFlake](./logo.png) [![BSD-3-Clause][s1]][li] -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FSnowFlakeOS%2FSnowFlake.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FSnowFlakeOS%2FSnowFlake?ref=badge_shield) [s1]: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg @@ -12,17 +11,17 @@ Technology is free, SnowFlakeOS ## Library used - utf16_literal (https://github.com/thepowersgang/rust_os/tree/master/Bootloaders/libuefi/utf16_literal) - uefi (forked, https://github.com/thepowersgang/rust_os/tree/master/Bootloaders/libuefi) +- slab_allocator (https://github.com/redox-os/slab_allocator) ## TODO ### Boot2Snow (x86_64, UEFI) - [x] Add uefi_alloc support - [x] Load kernel from disk -- [x] Set virtual memory map - [ ] Basical UI - [ ] Enable boot timeout -- [ ] Add fs.rs (Filesystem) ### SnowKernel -- [ ] Add alloc support +- [x] Kernel heap +- [ ] IDT - [ ] Better GUI library support - [ ] Add modular support - [ ] Multitasking support @@ -37,8 +36,6 @@ Technology is free, SnowFlakeOS ## Building Requirements to build - Rust (https://www.rust-lang.org) -- NASM (http://www.nasm.us/) -- GCC Toolchain or GCC (https://gcc.gnu.org/) ### Windows Will be added later @@ -54,7 +51,7 @@ $ sh x86_64-linux_env.sh ``` #### Arch Linux ``` -$ pacman -S qemu nasm mtools +$ pacman -S qemu mtools $ git clone https://github.com/SnowFlake/SnowFlake.git $ cd SnowWhite $ make run @@ -66,6 +63,3 @@ $ make run - https://github.com/redox-os/orbclient (MIT License) - https://github.com/redox-os/uefi (MIT License) - https://github.com/system76/firmware-update - -## License -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FSnowFlakeOS%2FSnowFlake.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FSnowFlakeOS%2FSnowFlake?ref=badge_large) \ No newline at end of file diff --git a/arch/x86_64/boot2snow/Makefile b/arch/x86_64/boot2snow/Makefile deleted file mode 100755 index 9f9b32d..0000000 --- a/arch/x86_64/boot2snow/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -arch ?= x86_64 -target_arch ?= $(arch)-boot2snow - -LD = $(arch)-efi-pe-ld -AS = $(arch)-efi-pe-as -AR = $(arch)-efi-pe-ar -STRIP = $(arch)-efi-pe-strip -OBJDUMP = $(arch)-efi-pe-objdump - -CARGO = xargo -CARGOFLAGS = --target $(target_arch) --release -- -C soft-float - -BUILD_DIR = ../../../build/arch/x86_64/boot2snow - -boot2snow = $(BUILD_DIR)/bootx64.efi -boot2snow_obj = $(BUILD_DIR)/target/$(target_arch)/release/libBoot2Snow.a - -objs = $(boot2snow_obj).o - -.PHONY: all - -all: $(boot2snow) - -$(boot2snow): $(objs) - @mkdir -p $(shell dirname $@) - @$(LD) --gc-sections --oformat pei-x86-64 --subsystem 10 -pie -e _start $< -o $@ - @$(OBJDUMP) -d $@ > $@.dsm - @$(STRIP) $@ - -$(boot2snow_obj).o: $(boot2snow_obj) - @cd $(shell dirname $<) && $(AR) x libBoot2Snow.a - @$(LD) -r $(shell dirname $<)/*.o -o $@ - -$(boot2snow_obj): - @mkdir -p $(shell dirname $@) - @RUST_TARGET_PATH=$(abspath .) CARGO_TARGET_DIR=$(BUILD_DIR)/target $(CARGO) rustc --lib $(CARGOFLAGS) -C lto --emit link=$@ diff --git a/arch/x86_64/boot2snow/x86_64-boot2snow.json b/arch/x86_64/boot2snow/x86_64-boot2snow.json deleted file mode 100755 index 4516b44..0000000 --- a/arch/x86_64/boot2snow/x86_64-boot2snow.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "cpu": "x86-64", - "data-layout": "e-m:w-i64:64-f80:128-n8:16:32:64-S128", - "llvm-target": "x86_64-pc-windows-gnu", - "target-endian": "little", - "target-pointer-width": "64", - "target-word-size": "64", - "target-c-int-width": "32", - "os": "boot2snow", - "arch": "x86_64", - "pre-link-args": [], - "no-compiler-rt": true, - "disable-redzone": true, - "eliminate-frame-pointer": false, - "linker-flavor": "ld", - "morestack": false -} diff --git a/arch/x86_64/linker.ld b/arch/x86_64/linker.ld deleted file mode 100755 index 53e2518..0000000 --- a/arch/x86_64/linker.ld +++ /dev/null @@ -1,43 +0,0 @@ -/* The bootloader will look at this image and start execution at the symbol - designated as the entry point. */ -ENTRY(_start) - -/* Tell where the various sections of the object files will be put in the final - kernel image. */ -SECTIONS -{ - /* Begin putting sections at 1 MiB, a conventional place for kernels to be - loaded at by the bootloader. */ - . = 1M; - - /* First put the multiboot header, as it is required to be put very early - early in the image or the bootloader won't recognize the file format. - Next we'll put the .text section. */ - .text BLOCK(4K) : ALIGN(4K) - { - *(.text) - } - - /* Read-only data. */ - .rodata BLOCK(4K) : ALIGN(4K) - { - *(.rodata) - } - - /* Read-write data (initialized) */ - .data BLOCK(4K) : ALIGN(4K) - { - *(.data) - } - - /* Read-write data (uninitialized) and stack */ - .bss BLOCK(4K) : ALIGN(4K) - { - *(COMMON) - *(.bss) - *(.bootstrap_stack) - } - - /* The compiler may produce other sections, by default it will put them in - a segment with the same name. Simply add stuff here as needed. */ -} \ No newline at end of file diff --git a/arch/x86_64/boot2snow/Cargo.toml b/boot2snow/Cargo.toml similarity index 63% rename from arch/x86_64/boot2snow/Cargo.toml rename to boot2snow/Cargo.toml index 0e95c9c..c4c47be 100755 --- a/arch/x86_64/boot2snow/Cargo.toml +++ b/boot2snow/Cargo.toml @@ -8,10 +8,12 @@ git = "https://github.com/rust-lang-nursery/compiler-builtins" features = ["mem"] [dependencies] -uefi = {git = "https://github.com/SnowFlakeOS/uefi"} -uefi_alloc = {git = "https://github.com/SnowFlakeOS/uefi_alloc"} +uefi = {git = "https://github.com/SnowFlakeOS/uefi", rev = "ad7346f"} +uefi_alloc = {git = "https://github.com/SnowFlakeOS/uefi_alloc", rev = "9e75de6"} rlibc = "1.0" utf16_literal = "0.1.0" +bitflags = "1" +x86_64 = "0.1.2" [lib] name = "Boot2Snow" diff --git a/boot2snow/Makefile b/boot2snow/Makefile new file mode 100644 index 0000000..b44c731 --- /dev/null +++ b/boot2snow/Makefile @@ -0,0 +1,63 @@ +arch ?= x86_64 +target_arch ?= $(arch)-boot2snow + +BUILD_DIR = ../build/boot2snow +PREFIX = $(shell readlink -f ..//prefix) + +LD = $(PREFIX)/bin/$(arch)-efi-pe-ld + +CARGO = xargo +CARGOFLAGS = --target $(target_arch) --release -- -C soft-float + +BINUTILS = 2.30 + +boot2snow = $(BUILD_DIR)/bootx64.efi +boot2snow_obj = $(BUILD_DIR)/target/$(target_arch)/release/libBoot2Snow.a + +objs = $(boot2snow_obj).o + +.PHONY: all + +all: $(boot2snow) + +$(boot2snow): $(objs) $(LD) + @mkdir -p $(shell dirname $@) + @$(LD) \ + --oformat pei-x86-64 \ + --dll \ + --image-base 0 \ + --section-alignment 32 \ + --file-alignment 32 \ + --major-os-version 0 \ + --minor-os-version 0 \ + --major-image-version 0 \ + --minor-image-version 0 \ + --major-subsystem-version 0 \ + --minor-subsystem-version 0 \ + --subsystem 10 \ + --heap 0,0 \ + --stack 0,0 \ + --pic-executable \ + --entry _start \ + --no-insert-timestamp \ + $< -o $@ + +$(boot2snow_obj).o: $(boot2snow_obj) + @cd $(shell dirname $<) && $(AR) x libBoot2Snow.a + @ld -r $(shell dirname $<)/*.o -o $@ + +$(boot2snow_obj): + @mkdir -p $(shell dirname $@) + @RUST_TARGET_PATH=$(abspath .) CARGO_TARGET_DIR=$(BUILD_DIR)/target $(CARGO) rustc --lib $(CARGOFLAGS) -C lto --emit link=$@ + +$(PREFIX)/binutils-$(BINUTILS).tar.gz: + @mkdir -p $(shell dirname $@) + @curl -o $@ https://ftp.gnu.org/gnu/binutils/binutils-$(BINUTILS).tar.gz + +$(LD): $(PREFIX)/binutils-$(BINUTILS).tar.gz + @tar -zxvf $< -C $(PREFIX) + @mkdir -p $(PREFIX)/build + @cd $(PREFIX)/build && \ + ../binutils-$(BINUTILS)/configure --target="$(arch)-efi-pe" --disable-werror --prefix="$(PREFIX)" && \ + make all-ld -j `nproc` && \ + make install-ld -j `nproc` diff --git a/arch/x86_64/boot2snow/Xargo.toml b/boot2snow/Xargo.toml similarity index 100% rename from arch/x86_64/boot2snow/Xargo.toml rename to boot2snow/Xargo.toml diff --git a/boot2snow/rust-toolchain b/boot2snow/rust-toolchain new file mode 100644 index 0000000..577d7d7 --- /dev/null +++ b/boot2snow/rust-toolchain @@ -0,0 +1 @@ +nightly-2018-03-31 \ No newline at end of file diff --git a/arch/x86_64/boot2snow/src/boot2snow.rs b/boot2snow/src/boot2snow.rs similarity index 51% rename from arch/x86_64/boot2snow/src/boot2snow.rs rename to boot2snow/src/boot2snow.rs index 0765d3f..dcc8a16 100755 --- a/arch/x86_64/boot2snow/src/boot2snow.rs +++ b/boot2snow/src/boot2snow.rs @@ -1,16 +1,34 @@ -use core::mem::size_of; +// ======================================================================= +// Copyleft SnowFlakeOS Team 2018-∞. +// Distributed under the terms of the 3-Clause BSD License. +// (See accompanying file LICENSE or copy at +// https://opensource.org/licenses/BSD-3-Clause) +// ======================================================================= + +//! Some code was borrowed from [Tifflin Bootloader](https://github.com/thepowersgang/rust_os) + +use core::{mem, slice, ptr}; use color::*; use {EntryPoint, elf, kernel_proto}; use conf::Configuration; +use uefi::CStr16; use uefi::status::*; use uefi::boot_services::protocols; use uefi::boot_services::{BootServices, AllocateType, MemoryDescriptor, MemoryType}; +use memory_map::{MM_BASE, memory_map}; +use paging::paging; +use string::wstr; + +static mut KERNEL_BASE: usize = 0; +static mut KERNEL_SIZE: usize = 0; +static STACK_BASE: usize = 0xFFFFFF0000080000; +static STACK_SIZE: usize = 0x1F000; use {PATH_CONFIG, PATH_FALLBACK_KERNEL}; @@ -41,47 +59,24 @@ pub extern fn init() -> Result<(), ()> { Ok(c) => c, Err(e) => panic!("Failed to load config file: {:?}", e), }; - + // - Load the kernel. - let entrypoint = load_kernel_file(boot_services, &system_volume_root, &config.kernel).expect("Unable to load kernel"); + let entrypoint = load_kernel_file(&system_volume_root, &config.kernel).expect("Unable to load kernel"); + let sections = load_kernel_sections(&system_volume_root, &config.kernel).expect("Unable to load sections"); // Save memory map - let (map_size, map_key, ent_size, ent_ver, map) = { - let mut map_size = 0; - let mut map_key = 0; - let mut ent_size = 0; - let mut ent_ver = 0; - match unsafe { (boot_services.get_memory_map)(&mut map_size, ::core::ptr::null_mut(), &mut map_key, &mut ent_size, &mut ent_ver) } - { - SUCCESS => {}, - BUFFER_TOO_SMALL => {}, - e => panic!("Sorry, get_memory_map() Failed :( - {:?}", e), - } - - assert_eq!( ent_size, size_of::() ); - let mut map; - loop - { - map = boot_services.allocate_pool_vec( MemoryType::LoaderData, map_size / ent_size ).unwrap(); - match unsafe { (boot_services.get_memory_map)(&mut map_size, map.as_mut_ptr(), &mut map_key, &mut ent_size, &mut ent_ver) } - { - SUCCESS => break, - BUFFER_TOO_SMALL => continue, - e => panic!("get_memory_map 2 - {:?}", e), - } - } - unsafe { - map.set_len( map_size / ent_size ); - } - - (map_size, map_key, ent_size, ent_ver, map) - }; + let (map_size, map_key, ent_size, ent_ver, map) = unsafe { memory_map() }; unsafe { (boot_services.exit_boot_services)(image_handle, map_key).expect("Sorry, exit_boot_services() failed"); - (runtime_services.set_virtual_address_map)(map_size, ent_size, ent_ver, map.as_ptr()).expect("Sorry, set_virtual_address_map() failed :("); + (runtime_services.set_virtual_address_map)(map_size, ent_size, ent_ver, map).expect("Sorry, set_virtual_address_map() failed :("); } + unsafe { + asm!("cli" : : : "memory" : "intel", "volatile"); + paging(); + } + let video_info = kernel_proto::VideoInfo { physbaseptr: gop.mode.frame_buffer_base as *mut Color, xresolution: unsafe { (*gop.mode.info).horizontal_resolution }, @@ -94,23 +89,53 @@ pub extern fn init() -> Result<(), ()> { // TODO: Get from the configuration cmdline_ptr: 1 as *const u8, cmdline_len: 0, + + elf_sections: Some(sections), + kernel_base: unsafe { KERNEL_BASE }, + kernel_size: unsafe { KERNEL_SIZE }, + stack_base: STACK_BASE, + stack_size: STACK_BASE + STACK_SIZE, - map_addr: map.as_ptr() as usize as u64, - map_entnum: map.len() as u32, - map_entsz: size_of::() as u32, + map_addr: MM_BASE, + map_len: (map_size / ent_size) as u32, + descriptor_size: mem::size_of::() as u32, - video_info: &video_info + video_info: &video_info }; // - Execute kernel (passing a magic value and general boot information) + unsafe { asm!("mov rsp, $0" : : "r"(STACK_BASE + STACK_SIZE) : "memory" : "intel", "volatile") }; entrypoint(0x71FF0EF1, &boot_info); } Ok(()) } -fn load_kernel_file(boot_services: &::uefi::boot_services::BootServices, sys_vol: &protocols::File, filename: &::uefi::CStr16) -> Result -{ +fn load_kernel_sections(sys_vol: &protocols::File, filename: &CStr16) -> Result, Status> { + let mut kernel_file = match sys_vol.open_read(filename) { + Ok(k) => k, + Err(e) => panic!("Failed to open kernel '{}' - {:?}", filename, e), + }; + + // Load kernel from this file (ELF). + let elf_hdr = { + let mut hdr = elf::ElfHeader::default(); + // SAFE: Converts to POD for read + kernel_file.read( unsafe { slice::from_raw_parts_mut( &mut hdr as *mut _ as *mut u8, mem::size_of::() ) } ).expect("ElfHeader read"); + hdr + }; + + let slice: &[elf::PhEnt] = unsafe { + let ptr = (&elf_hdr as *const _ as usize + elf_hdr.e_phoff as usize) as *const elf::PhEnt; + slice::from_raw_parts(ptr, elf_hdr.e_phnum as usize) + }; + + Ok(elf::PhEntIter(slice)) +} + +fn load_kernel_file(sys_vol: &protocols::File, filename: &CStr16) -> Result { + let boot_services = get_boot_services(); + let mut kernel_file = match sys_vol.open_read(filename) { Ok(k) => k, Err(e) => panic!("Failed to open kernel '{}' - {:?}", filename, e), @@ -120,36 +145,39 @@ fn load_kernel_file(boot_services: &::uefi::boot_services::BootServices, sys_vol let elf_hdr = { let mut hdr = elf::ElfHeader::default(); // SAFE: Converts to POD for read - kernel_file.read( unsafe { ::core::slice::from_raw_parts_mut( &mut hdr as *mut _ as *mut u8, size_of::() ) } ).expect("ElfHeader read"); + kernel_file.read( unsafe { slice::from_raw_parts_mut( &mut hdr as *mut _ as *mut u8, mem::size_of::() ) } ).expect("ElfHeader read"); hdr }; elf_hdr.check_header(); - for i in 0 .. elf_hdr.e_phnum - { + for i in 0 .. elf_hdr.e_phnum { let mut ent = elf::PhEnt::default(); - kernel_file.set_position(elf_hdr.e_phoff as u64 + (i as usize * size_of::()) as u64 ).expect("PhEnt seek"); + kernel_file.set_position(elf_hdr.e_phoff + (i as usize * mem::size_of::()) as u64).expect("PhEnt seek"); // SAFE: Converts to POD for read - kernel_file.read( unsafe { ::core::slice::from_raw_parts_mut( &mut ent as *mut _ as *mut u8, size_of::() ) } ).expect("PhEnt read"); + kernel_file.read( unsafe { slice::from_raw_parts_mut( &mut ent as *mut _ as *mut u8, mem::size_of::() ) } ).expect("PhEnt read"); - if ent.p_type == 1 - { + if ent.p_type == 1 { println!("- {:#x}+{:#x} loads +{:#x}+{:#x}", ent.p_paddr, ent.p_memsz, ent.p_offset, ent.p_filesz ); + + unsafe { + KERNEL_BASE = ent.p_vaddr as usize; + KERNEL_SIZE = ent.p_memsz as usize; + } - let mut addr = ent.p_paddr as u64; + let mut addr = ent.p_paddr; // SAFE: Correct call to FFI unsafe { (boot_services.allocate_pages)( AllocateType::Address, MemoryType::LoaderData, - ent.p_memsz as usize / 0x4096, + (ent.p_memsz + 0xFFF) as usize / 0x1000, &mut addr ).expect("Allocating pages for program segment") }; // SAFE: This memory has just been allocated by the above - let data_slice = unsafe { ::core::slice::from_raw_parts_mut(ent.p_paddr as usize as *mut u8, ent.p_memsz as usize) }; + let data_slice = unsafe { slice::from_raw_parts_mut(addr as usize as *mut u8, ent.p_memsz as usize) }; kernel_file.set_position(ent.p_offset as u64).expect("seek segment"); kernel_file.read( &mut data_slice[.. ent.p_filesz as usize] ).expect("read segment"); for b in &mut data_slice[ent.p_filesz as usize .. ent.p_memsz as usize] { @@ -158,6 +186,6 @@ fn load_kernel_file(boot_services: &::uefi::boot_services::BootServices, sys_vol } } // SAFE: Assuming that the executable is sane, and that it follows the correct calling convention - Ok(unsafe { ::core::mem::transmute(elf_hdr.e_entry as usize) }) + Ok(unsafe { mem::transmute(elf_hdr.e_entry as usize) }) } diff --git a/arch/x86_64/boot2snow/src/conf.rs b/boot2snow/src/conf.rs similarity index 90% rename from arch/x86_64/boot2snow/src/conf.rs rename to boot2snow/src/conf.rs index fb573b2..aee7374 100755 --- a/arch/x86_64/boot2snow/src/conf.rs +++ b/boot2snow/src/conf.rs @@ -1,3 +1,5 @@ +//! Some code was borrowed from [Tifflin Bootloader](https://github.com/thepowersgang/rust_os) + use uefi::boot_services::protocols; use uefi::boot_services::BootServices; use uefi::{CStr16, @@ -32,4 +34,4 @@ impl<'bs> Configuration<'bs> Err(e) => Err(e), } } -} +} \ No newline at end of file diff --git a/arch/x86_64/boot2snow/src/image/bmp.rs b/boot2snow/src/image/bmp.rs similarity index 96% rename from arch/x86_64/boot2snow/src/image/bmp.rs rename to boot2snow/src/image/bmp.rs index 0b41c83..a8e7163 100755 --- a/arch/x86_64/boot2snow/src/image/bmp.rs +++ b/boot2snow/src/image/bmp.rs @@ -1,3 +1,5 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) + use alloc::string::{String, ToString}; use alloc::vec::Vec; diff --git a/arch/x86_64/boot2snow/src/image/mod.rs b/boot2snow/src/image/mod.rs similarity index 96% rename from arch/x86_64/boot2snow/src/image/mod.rs rename to boot2snow/src/image/mod.rs index 97c3fd1..bf7e8a2 100755 --- a/arch/x86_64/boot2snow/src/image/mod.rs +++ b/boot2snow/src/image/mod.rs @@ -1,3 +1,5 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) + use alloc::boxed::Box; use alloc::string::{String, ToString}; use core::cmp; diff --git a/arch/x86_64/boot2snow/src/io.rs b/boot2snow/src/io.rs similarity index 94% rename from arch/x86_64/boot2snow/src/io.rs rename to boot2snow/src/io.rs index e655649..a08ee2d 100755 --- a/arch/x86_64/boot2snow/src/io.rs +++ b/boot2snow/src/io.rs @@ -1,3 +1,5 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) + use core::{char, mem}; use core::fmt::{self, Write}; diff --git a/arch/x86_64/boot2snow/src/lib.rs b/boot2snow/src/lib.rs similarity index 78% rename from arch/x86_64/boot2snow/src/lib.rs rename to boot2snow/src/lib.rs index 0d44c2d..e36a94d 100755 --- a/arch/x86_64/boot2snow/src/lib.rs +++ b/boot2snow/src/lib.rs @@ -1,7 +1,8 @@ +//! Some code was borrowed from [Tifflin Bootloader](https://github.com/thepowersgang/rust_os) + #![no_std] #![feature(alloc)] #![feature(asm)] -#![feature(compiler_builtins_lib)] #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(global_allocator)] @@ -22,11 +23,14 @@ use uefi::{SimpleTextOutputInterface, SystemTable, Status}; +#[macro_use] +extern crate bitflags; + #[macro_use] extern crate alloc; -extern crate compiler_builtins; extern crate uefi; extern crate uefi_alloc; +extern crate x86_64; extern crate utf16_literal; #[macro_use] @@ -38,14 +42,16 @@ mod boot2snow; mod conf; mod io; mod string; +mod memory_map; +mod paging; -#[path="../../../../share/elf.rs"] +#[path="../../share/elf.rs"] mod elf; -#[path="../../../../share/uefi_proto.rs"] +#[path="../../share/uefi_proto.rs"] mod kernel_proto; -#[path="../../../../share/color.rs"] +#[path="../../share/color.rs"] mod color; #[global_allocator] @@ -61,7 +67,7 @@ static mut S_RUNTIME_SERVICES: *const RuntimeServices = 0 as *const _; static mut S_GRAPHICS_OUTPUT: *const GraphicsOutput = 0 as *const _; static mut S_IMAGE_HANDLE: Handle = 0 as *mut _; -pub type EntryPoint = extern "cdecl" fn(usize, *const kernel_proto::Info) -> !; +pub type EntryPoint = extern "C" fn(usize, *const kernel_proto::Info) -> !; pub fn get_conout() -> &'static SimpleTextOutputInterface { unsafe { &*S_CONOUT } @@ -142,14 +148,14 @@ pub extern "win64" fn _start(image_handle: Handle, system_table: &SystemTable) - let gop = GraphicsOutput::new(get_boot_services()).unwrap(); { - if let Err(err) = set_text_mode(system_table.con_out).into_result() { - println!("Sorry, set_text_mode() Failed :( {:?}", err); - } - if let Err(err) = set_graphics_mode(gop).into_result() { println!("Sorry, set_graphics_mode() Failed :( {:?}", err); } + if let Err(err) = set_text_mode(system_table.con_out).into_result() { + println!("Sorry, set_text_mode() Failed :( {:?}", err); + } + unsafe { S_GRAPHICS_OUTPUT = gop }; if let Err(err) = boot2snow::init().into_result() { @@ -161,3 +167,25 @@ pub extern "win64" fn _start(image_handle: Handle, system_table: &SystemTable) - SUCCESS } +#[no_mangle] +pub extern "C" fn memcpy(dst: *mut u8, src: *const u8, count: usize) { + unsafe { + asm!("rep movsb" : : "{rcx}" (count), "{rdi}" (dst), "{rsi}" (src) : "rcx", "rsi", "rdi" : "volatile"); + } +} + +#[no_mangle] +pub extern "C" fn memset(dst: *mut u8, val: u8, count: usize) { + unsafe { + asm!("rep stosb" : : "{rcx}" (count), "{rdi}" (dst), "{al}" (val) : "rcx", "rdi" : "volatile"); + } +} + +#[no_mangle] +pub extern "C" fn memcmp(dst: *mut u8, src: *const u8, count: usize) -> isize { + unsafe { + let rv: isize; + asm!("repnz cmpsb ; movq $$0, $0 ; ja 1f; jb 2f; jmp 3f; 1: inc $0 ; jmp 3f; 2: dec $0; 3:" : "=r" (rv) : "{rcx}" (count), "{rdi}" (dst), "{rsi}" (src) : "rcx", "rsi", "rdi" : "volatile"); + rv + } +} \ No newline at end of file diff --git a/arch/x86_64/boot2snow/src/macros.rs b/boot2snow/src/macros.rs similarity index 100% rename from arch/x86_64/boot2snow/src/macros.rs rename to boot2snow/src/macros.rs diff --git a/boot2snow/src/memory_map.rs b/boot2snow/src/memory_map.rs new file mode 100644 index 0000000..5da320b --- /dev/null +++ b/boot2snow/src/memory_map.rs @@ -0,0 +1,83 @@ +//! Some code was borrowed from [Redox OS Bootloader for EFI](https://github.com/redox-os/bootloader-efi) + +use core::{mem, ptr}; +use uefi::boot_services::{MemoryDescriptor, MemoryType}; +use get_boot_services; + +pub static MM_BASE: u64 = 0x500; +static MM_SIZE: u64 = 0x4B00; + +/// Memory does not exist +pub const MEMORY_AREA_NULL: u32 = 0; + +/// Memory is free to use +pub const MEMORY_AREA_FREE: u32 = 1; + +/// Memory is reserved +pub const MEMORY_AREA_RESERVED: u32 = 2; + +/// Memory is used by ACPI, and can be reclaimed +pub const MEMORY_AREA_ACPI: u32 = 3; + +/// A memory map area +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct MemoryArea { + pub base_addr: u64, + pub length: u64, + pub _type: u32, + pub acpi: u32 +} + +pub unsafe fn memory_map() -> (usize, usize, usize, u32, *mut MemoryDescriptor) { + let boot_services = get_boot_services(); + + ptr::write_bytes(MM_BASE as *mut u8, 0, MM_SIZE as usize); + + let mut map: [u8; 65536] = [0; 65536]; + let mut map_size = map.len(); + let mut map_key = 0; + let mut descriptor_size = 0; + let mut descriptor_version = 0; + let _ = (boot_services.get_memory_map)( + &mut map_size, + map.as_mut_ptr() as *mut MemoryDescriptor, + &mut map_key, + &mut descriptor_size, + &mut descriptor_version + ); + + if descriptor_size >= mem::size_of::() { + for i in 0..map_size/descriptor_size { + let descriptor_ptr = map.as_ptr().offset((i * descriptor_size) as isize); + let descriptor = & *(descriptor_ptr as *const MemoryDescriptor); + let descriptor_type: MemoryType = mem::transmute(descriptor.ty); + + let bios_type = match descriptor_type { + MemoryType::LoaderCode | + MemoryType::LoaderData | + MemoryType::BootServicesCode | + MemoryType::BootServicesData | + MemoryType::ConventionalMemory => { + MEMORY_AREA_FREE + }, + _ => { + MEMORY_AREA_RESERVED + } + }; + + let bios_area = MemoryArea { + base_addr: descriptor.physical_start, + length: descriptor.number_of_pages * 4096, + _type: bios_type, + acpi: 0, + }; + + ptr::write((MM_BASE as *mut MemoryArea).offset(i as isize), bios_area); + } + } else { + println!("Unknown memory descriptor size: {}", descriptor_size); + } + + (map_size, map_key, descriptor_size, descriptor_version, map.as_mut_ptr() as *mut MemoryDescriptor) +} diff --git a/boot2snow/src/paging.rs b/boot2snow/src/paging.rs new file mode 100644 index 0000000..f54aa47 --- /dev/null +++ b/boot2snow/src/paging.rs @@ -0,0 +1,58 @@ +//! Some code was borrowed from [Redox OS Bootloader for EFI](https://github.com/redox-os/bootloader-efi) + +use core::ptr; +use x86_64::PhysicalAddress; +use x86_64::registers::control_regs::*; +use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr}; + +static PT_BASE: u64 = 0x70000; + +pub unsafe fn paging() { + // Zero PML4, PDP, and 4 PD + ptr::write_bytes(PT_BASE as *mut u8, 0, 6 * 4096); + + let mut base = PT_BASE; + + // Link first PML4 and second to last PML4 to PDP + ptr::write(base as *mut u64, 0x71000 | 1 << 1 | 1); + ptr::write((base + 510*8) as *mut u64, 0x71000 | 1 << 1 | 1); + // Link last PML4 to PML4 + ptr::write((base + 511*8) as *mut u64, 0x70000 | 1 << 1 | 1); + + // Move to PDP + base += 4096; + + // Link first four PDP to PD + ptr::write(base as *mut u64, 0x72000 | 1 << 1 | 1); + ptr::write((base + 8) as *mut u64, 0x73000 | 1 << 1 | 1); + ptr::write((base + 16) as *mut u64, 0x74000 | 1 << 1 | 1); + ptr::write((base + 24) as *mut u64, 0x75000 | 1 << 1 | 1); + + // Move to PD + base += 4096; + + // Link all PD's (512 per PDP, 2MB each) + let mut entry = 1 << 7 | 1 << 1 | 1; + for i in 0..4*512 { + ptr::write((base + i*8) as *mut u64, entry); + entry += 0x200000; + } + + // Enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension + let mut cr4 = cr4(); + cr4 |= Cr4::ENABLE_SSE | Cr4::ENABLE_GLOBAL_PAGES | Cr4::ENABLE_PAE | Cr4::ENABLE_PSE; + cr4_write(cr4); + + // Enable Long mode and NX bit + let mut efer = rdmsr(IA32_EFER); + efer |= 1 << 11 | 1 << 8; + wrmsr(IA32_EFER, efer); + + // Set new page map + cr3_write(PhysicalAddress(PT_BASE)); + + // Enable paging, write protect kernel, protected mode + let mut cr0 = cr0(); + cr0 |= Cr0::ENABLE_PAGING | Cr0::WRITE_PROTECT | Cr0::PROTECTED_MODE; + cr0_write(cr0); +} \ No newline at end of file diff --git a/arch/x86_64/boot2snow/src/panic.rs b/boot2snow/src/panic.rs similarity index 73% rename from arch/x86_64/boot2snow/src/panic.rs rename to boot2snow/src/panic.rs index 2bb1472..0953e41 100755 --- a/arch/x86_64/boot2snow/src/panic.rs +++ b/boot2snow/src/panic.rs @@ -1,8 +1,7 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) + use core::fmt; -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. #[lang = "eh_personality"] #[no_mangle] pub extern fn rust_eh_personality() {} @@ -10,7 +9,6 @@ pub extern fn rust_eh_personality() {} #[no_mangle] pub extern fn ___chkstk_ms() {} -// This function may be needed based on the compilation target. #[lang = "eh_unwind_resume"] #[no_mangle] pub extern fn rust_eh_unwind_resume() { diff --git a/arch/x86_64/boot2snow/src/string.rs b/boot2snow/src/string.rs similarity index 83% rename from arch/x86_64/boot2snow/src/string.rs rename to boot2snow/src/string.rs index 40edb25..612ac78 100644 --- a/arch/x86_64/boot2snow/src/string.rs +++ b/boot2snow/src/string.rs @@ -1,3 +1,5 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) + use alloc::{String, Vec}; use core::char; diff --git a/boot2snow/x86_64-boot2snow.json b/boot2snow/x86_64-boot2snow.json new file mode 100644 index 0000000..f638779 --- /dev/null +++ b/boot2snow/x86_64-boot2snow.json @@ -0,0 +1,29 @@ + +{ + "linker": "false", + "linker-flavor": "ld", + "llvm-target": "x86_64-efi-pe", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "os": "efi", + "env": "pe", + "vendor": "unknown", + "target-family": "none", + "pre-link-args": ["-m64", "-nostdlib", "-static"], + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", + "dynamic-linking": false, + "executables": false, + "relocation-model": "pic", + "code-model": "kernel", + "disable-redzone": true, + "eliminate-frame-pointer": false, + "exe-suffix": "", + "has-rpath": false, + "no-compiler-rt": true, + "no-default-libraries": true, + "position-independent-executables": true, + "has-elf-tls": false +} \ No newline at end of file diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index c702a9c..96f4c34 100755 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -11,5 +11,6 @@ rlibc = "1.0" volatile = "0.1.0" spin = "0.4.5" x86_64 = "0.1.2" -bitflags = "1.0.1" -uefi = {git = "https://github.com/SnowFlakeOS/uefi"} \ No newline at end of file +bitflags = "1" +once = "0.3.3" +slab_allocator = { git = "https://github.com/redox-os/slab_allocator.git", rev = "0a53a0b" } \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile index 8594b6d..954b175 100755 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,17 +1,16 @@ arch ?= x86_64 target_arch ?= $(arch)-snowflake -LD = $(arch)-elf-ld -AS = $(arch)-elf-as -AR = $(arch)-elf-ar -STRIP = $(arch)-elf-strip -OBJDUMP = $(arch)-elf-objdump -OBJCOPY = $(arch)-elf-objcopy +LD = ld +AR = ar +STRIP = strip +OBJDUMP = objdump +OBJCOPY = objcopy CARGO = xargo BUILD_DIR = ../build/kernel -linker_script = ../arch/$(arch)/linker.ld +linker_script = arch/$(arch)/linker.ld kernel = $(BUILD_DIR)/kernel.bin kernel_obj = $(BUILD_DIR)/target/$(target_arch)/release/libSnowKernel.a @@ -21,12 +20,9 @@ all: $(kernel) $(kernel): $(kernel_obj) @mkdir -p $(shell dirname $@) - @$(LD) -n --gc-sections -T $(linker_script) $< -o $@.tmp -z max-page-size=0x1000 - @$(OBJDUMP) -d $@.tmp > $@.dsm - @$(OBJCOPY) --only-keep-debug $@.tmp $@.sym - @$(OBJCOPY) $@.tmp -F elf32-i386 $@ - @$(STRIP) -g $@ - + @$(LD) -n --gc-sections -T $(linker_script) $< -o $@ -z max-page-size=0x1000 + @$(OBJDUMP) -d $@ > $@.dsm + @$(OBJCOPY) --only-keep-debug $@ $@.sym $(kernel_obj): @mkdir -p $(shell dirname $@) - @RUST_TARGET_PATH=$(abspath .) CARGO_TARGET_DIR=$(BUILD_DIR)/target $(CARGO) build --target $(target_arch) --release + @RUST_TARGET_PATH=$(abspath .) CARGO_TARGET_DIR=$(BUILD_DIR)/target CFLAGS=-ffreestanding $(CARGO) rustc --target $(target_arch) --release -- -C soft-float -C debuginfo=2 --emit link=$@ diff --git a/kernel/Xargo.toml b/kernel/Xargo.toml new file mode 100644 index 0000000..c96e63a --- /dev/null +++ b/kernel/Xargo.toml @@ -0,0 +1 @@ +[dependencies.alloc] \ No newline at end of file diff --git a/kernel/arch/x86_64/linker.ld b/kernel/arch/x86_64/linker.ld new file mode 100755 index 0000000..96708cb --- /dev/null +++ b/kernel/arch/x86_64/linker.ld @@ -0,0 +1,56 @@ +ENTRY(kmain) +OUTPUT_FORMAT(elf64-x86-64) + +SECTIONS { + . = 1M; + + . += SIZEOF_HEADERS; + . = ALIGN(4096); + + .text : AT(ADDR(.text)) { + __text_start = .; + *(.text*) + . = ALIGN(4096); + __text_end = .; + } + + .rodata : AT(ADDR(.rodata)) { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + + .data : AT(ADDR(.data)) { + __data_start = .; + *(.data*) + . = ALIGN(4096); + __data_end = .; + __bss_start = .; + *(.bss*) + . = ALIGN(4096); + __bss_end = .; + } + + .tdata : AT(ADDR(.tdata)) { + __tdata_start = .; + *(.tdata*) + . = ALIGN(4096); + __tdata_end = .; + __tbss_start = .; + *(.tbss*) + . += 8; + . = ALIGN(4096); + __tbss_end = .; + } + + __end = .; + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} \ No newline at end of file diff --git a/kernel/rust-toolchain b/kernel/rust-toolchain new file mode 100644 index 0000000..577d7d7 --- /dev/null +++ b/kernel/rust-toolchain @@ -0,0 +1 @@ +nightly-2018-03-31 \ No newline at end of file diff --git a/kernel/src/console.rs b/kernel/src/console.rs index 11476af..9de7c23 100644 --- a/kernel/src/console.rs +++ b/kernel/src/console.rs @@ -1,3 +1,10 @@ +// ======================================================================= +// Copyleft SnowFlakeOS Team 2018-∞. +// Distributed under the terms of the 3-Clause BSD License. +// (See accompanying file LICENSE or copy at +// https://opensource.org/licenses/BSD-3-Clause) +// ======================================================================= + use core::fmt::{self, Write}; use color::*; use display::Display; @@ -54,7 +61,14 @@ impl Console { let prompt = s.clone(); for c in prompt.chars() { - if self.x == self.w as i32 || c == '\n' { self.newline(); } else { + if self.y >= self.h as i32 { + unsafe { (*display).scroll(20, Color::rgb(0, 0, 0)) }; + self.y -= 20; + } + + if self.x == self.w as i32 || c == '\n' { + self.newline(); + } else { unsafe { (*display).char(self.x, self.y, c, color) }; self.x += 8; } @@ -63,6 +77,6 @@ impl Console { pub fn newline(&mut self) { self.x = 0; - self.y += 14; + self.y += 20; } } diff --git a/kernel/src/display.rs b/kernel/src/display.rs index 3be1f9a..052251f 100644 --- a/kernel/src/display.rs +++ b/kernel/src/display.rs @@ -1,3 +1,5 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) and [Redox OS Orbital Client Library](https://github.com/redox-os/orbclient) + use core::cmp; use color::*; diff --git a/kernel/src/entry.s b/kernel/src/entry.s deleted file mode 100644 index 3ab2036..0000000 --- a/kernel/src/entry.s +++ /dev/null @@ -1,28 +0,0 @@ -.global _start -.global _info -.global _magic -.intel_syntax noprefix -.code32 -.section .text - -_start: - push ecx - push edx - call _start_uefi - hlt - -.code64 -_start_uefi: - mov _magic, ecx - mov _info, edx - cmp ecx, 0x71FF0EF1 - jz start_uefi - hlt - -.section .data - -_info: - .quad 0 - .quad 0xFFFFFFFF -_magic: - .quad 0 \ No newline at end of file diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs index abae2b9..a91a826 100755 --- a/kernel/src/kmain.rs +++ b/kernel/src/kmain.rs @@ -1,19 +1,24 @@ +// ======================================================================= +// Copyleft SnowFlakeOS Team 2018-∞. +// Distributed under the terms of the 3-Clause BSD License. +// (See accompanying file LICENSE or copy at +// https://opensource.org/licenses/BSD-3-Clause) +// ======================================================================= + use core::ops::Try; -use uefi::runtime_services::RuntimeServices; +use core::mem; +use core::ptr; + use color::*; use kernel_proto::{Info, MemoryDescriptor}; use display::Display; use console::{Console, set_console}; -extern { - static _magic: usize; - static _info: *const Info; -} +use memory; #[no_mangle] -pub extern fn start_uefi() { - let magic = unsafe { _magic }; - let info = unsafe { &*_info }; +pub extern "C" fn kmain(magic: usize, boot_info: *const Info) -> ! { + let info = unsafe { &*boot_info }; let video_info = unsafe { &*(*info).video_info }; let resolutin_w = video_info.xresolution; @@ -23,33 +28,21 @@ pub extern fn start_uefi() { let vid_addr = video_info.physbaseptr; let mut display = Display::new(vid_addr, resolutin_w, resolutin_h); let mut console = Console::new(&mut display); - let map = info.map_addr as *const MemoryDescriptor; - set_console(&mut console); + let elf_sections = info.elf_sections; - enable_nxe_bit(); - enable_write_protect_bit(); + set_console(&mut console); display.rect(0, 0, resolutin_w, resolutin_h, Color::rgb(0, 0, 0)); println!("SnowKernel {}", env!("CARGO_PKG_VERSION")); + println!("Screen resolution is {}x{}", resolutin_w, resolutin_h); + println!("Kernel heap start : {:#x} | size : {:#x}", ::HEAP_START, ::HEAP_SIZE); + println!("Kernel start : {:#x} | end : {:#x}", info.kernel_base, info.kernel_base + info.kernel_size); - panic!("Test panic"); -} - -// https://github.com/phil-opp/blog_os/blob/post_10/src/lib.rs - -fn enable_nxe_bit() { - use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr}; - - let nxe_bit = 1 << 11; unsafe { - let efer = rdmsr(IA32_EFER); - wrmsr(IA32_EFER, efer | nxe_bit); + memory::init(0, (info.kernel_base + (info.kernel_size + 4095)/4096) * 4096); + ::ALLOCATOR.init(::HEAP_START, ::HEAP_SIZE); } -} -fn enable_write_protect_bit() { - use x86_64::registers::control_regs::{cr0, cr0_write, Cr0}; - - unsafe { cr0_write(cr0() | Cr0::WRITE_PROTECT) }; -} \ No newline at end of file + panic!("Test panic"); +} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 6bc026c..fa9412e 100755 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,15 +1,29 @@ -#![feature(lang_items)] // required for defining the panic handler -#![no_std] // don't link the Rust standard library -#![no_main] // disable all Rust-level entry points +#![no_std] +#![no_main] +#![feature(lang_items)] #![feature(try_trait)] #![feature(asm)] #![feature(const_fn)] #![feature(global_asm)] +#![feature(ptr_internals)] +#![feature(alloc)] +#![feature(allocator_api)] +#![feature(global_allocator)] +#![feature(concat_idents)] +#![feature(thread_local)] +#![feature(custom_attribute)] + +#[macro_use] +extern crate alloc; + +#[macro_use] +extern crate once; #[macro_use] extern crate bitflags; extern crate x86_64; -extern crate uefi; +extern crate spin; +extern crate slab_allocator; use core::ptr; @@ -20,6 +34,7 @@ pub mod panic; pub mod kmain; mod display; mod console; +mod memory; #[path="../../share/uefi_proto.rs"] mod kernel_proto; @@ -27,7 +42,13 @@ mod kernel_proto; #[path="../../share/color.rs"] mod color; -global_asm!(include_str!("entry.s")); +#[path="../../share/elf.rs"] +mod elf; + +use core::mem; +use slab_allocator::LockedHeap; + +const WORD_SIZE: usize = mem::size_of::(); #[no_mangle] pub extern "C" fn memcpy(dst: *mut u8, src: *const u8, count: usize) { @@ -50,4 +71,55 @@ pub extern "C" fn memcmp(dst: *mut u8, src: *const u8, count: usize) -> isize { asm!("repnz cmpsb ; movq $$0, $0 ; ja 1f; jb 2f; jmp 3f; 1: inc $0 ; jmp 3f; 2: dec $0; 3:" : "=r" (rv) : "{rcx}" (count), "{rdi}" (dst), "{rsi}" (src) : "rcx", "rsi", "rdi" : "volatile"); rv } -} \ No newline at end of file +} + +#[no_mangle] +pub unsafe extern fn memmove(dest: *mut u8, src: *const u8, + n: usize) -> *mut u8 { + if src < dest as *const u8 { + let n_usize: usize = n/WORD_SIZE; // Number of word sized groups + let mut i: usize = n_usize*WORD_SIZE; + + // Copy `WORD_SIZE` bytes at a time + while i != 0 { + i -= WORD_SIZE; + *((dest as usize + i) as *mut usize) = + *((src as usize + i) as *const usize); + } + + let mut i: usize = n; + + // Copy 1 byte at a time + while i != n_usize*WORD_SIZE { + i -= 1; + *((dest as usize + i) as *mut u8) = + *((src as usize + i) as *const u8); + } + } else { + let n_usize: usize = n/WORD_SIZE; // Number of word sized groups + let mut i: usize = 0; + + // Copy `WORD_SIZE` bytes at a time + let n_fast = n_usize*WORD_SIZE; + while i < n_fast { + *((dest as usize + i) as *mut usize) = + *((src as usize + i) as *const usize); + i += WORD_SIZE; + } + + // Copy 1 byte at a time + while i < n { + *((dest as usize + i) as *mut u8) = + *((src as usize + i) as *const u8); + i += 1; + } + } + + dest +} + +pub const HEAP_START: usize = 0o_000_000_700_000_0000; +pub const HEAP_SIZE: usize = 1 * 1024 * 1024; // 1 MB + +#[global_allocator] +static mut ALLOCATOR: LockedHeap = LockedHeap::empty(); \ No newline at end of file diff --git a/kernel/src/memory/bump.rs b/kernel/src/memory/bump.rs new file mode 100644 index 0000000..dd3a894 --- /dev/null +++ b/kernel/src/memory/bump.rs @@ -0,0 +1,129 @@ +//! # Bump frame allocator +//! Some code was borrowed from [Redox OS Kernel](https://github.com/redox-os/kernel) and [Phil Opp's Blog](http://os.phil-opp.com/allocating-frames.html) + +use super::PhysicalAddress; + +use super::{Frame, FrameAllocator, MemoryArea, MemoryAreaIter}; + +pub struct BumpAllocator { + next_free_frame: Frame, + current_area: Option<&'static MemoryArea>, + areas: MemoryAreaIter, + kernel_start: Frame, + kernel_end: Frame +} + +impl BumpAllocator { + pub fn new(kernel_start: usize, kernel_end: usize, memory_areas: MemoryAreaIter) -> Self { + let mut allocator = Self { + next_free_frame: Frame::containing_address(PhysicalAddress::new(0)), + current_area: None, + areas: memory_areas, + kernel_start: Frame::containing_address(PhysicalAddress::new(kernel_start)), + kernel_end: Frame::containing_address(PhysicalAddress::new(kernel_end)) + }; + allocator.choose_next_area(); + allocator + } + + fn choose_next_area(&mut self) { + self.current_area = self.areas.clone().filter(|area| { + let address = area.base_addr + area.length - 1; + Frame::containing_address(PhysicalAddress::new(address as usize)) >= self.next_free_frame + }).min_by_key(|area| area.base_addr); + + if let Some(area) = self.current_area { + let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize)); + if self.next_free_frame < start_frame { + self.next_free_frame = start_frame; + } + } + } +} + +impl FrameAllocator for BumpAllocator { + #[allow(unused)] + fn set_noncore(&mut self, noncore: bool) {} + + fn free_frames(&self) -> usize { + let mut count = 0; + + for area in self.areas.clone() { + let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize)); + let end_frame = Frame::containing_address(PhysicalAddress::new((area.base_addr + area.length - 1) as usize)); + for frame in Frame::range_inclusive(start_frame, end_frame) { + if frame >= self.kernel_start && frame <= self.kernel_end { + // Inside of kernel range + } else if frame >= self.next_free_frame { + // Frame is in free range + count += 1; + } else { + // Inside of used range + } + } + } + + count + } + + fn used_frames(&self) -> usize { + let mut count = 0; + + for area in self.areas.clone() { + let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize)); + let end_frame = Frame::containing_address(PhysicalAddress::new((area.base_addr + area.length - 1) as usize)); + for frame in Frame::range_inclusive(start_frame, end_frame) { + if frame >= self.kernel_start && frame <= self.kernel_end { + // Inside of kernel range + count += 1 + } else if frame >= self.next_free_frame { + // Frame is in free range + } else { + count += 1; + } + } + } + + count + } + + fn allocate_frames(&mut self, count: usize) -> Option { + if count == 0 { + None + } else if let Some(area) = self.current_area { + // "Clone" the frame to return it if it's free. Frame doesn't + // implement Clone, but we can construct an identical frame. + let start_frame = Frame{ number: self.next_free_frame.number }; + let end_frame = Frame { number: self.next_free_frame.number + (count - 1) }; + + // the last frame of the current area + let current_area_last_frame = { + let address = area.base_addr + area.length - 1; + Frame::containing_address(PhysicalAddress::new(address as usize)) + }; + + if end_frame > current_area_last_frame { + // all frames of current area are used, switch to next area + self.choose_next_area(); + } else if (start_frame >= self.kernel_start && start_frame <= self.kernel_end) + || (end_frame >= self.kernel_start && end_frame <= self.kernel_end) { + // `frame` is used by the kernel + self.next_free_frame = Frame { + number: self.kernel_end.number + 1 + }; + } else { + // frame is unused, increment `next_free_frame` and return it + self.next_free_frame.number += count; + return Some(start_frame); + } + // `frame` was not valid, try it again with the updated `next_free_frame` + self.allocate_frames(count) + } else { + None // no free frames left + } + } + + fn deallocate_frames(&mut self, _frame: Frame, _count: usize) { + //panic!("BumpAllocator::deallocate_frame: not supported: {:?}", frame); + } +} \ No newline at end of file diff --git a/kernel/src/memory/mod.rs b/kernel/src/memory/mod.rs new file mode 100644 index 0000000..fdd49db --- /dev/null +++ b/kernel/src/memory/mod.rs @@ -0,0 +1,221 @@ +//! # Memory management +//! Some code was borrowed from [Redox OS Kernel](https://github.com/redox-os/kernel) and [Phil Opp's Blog](http://os.phil-opp.com/allocating-frames.html) + +use self::bump::BumpAllocator; +use self::recycle::RecycleAllocator; + +use spin::Mutex; + +pub mod bump; +pub mod recycle; + +/// A physical address. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PhysicalAddress(usize); + +impl PhysicalAddress { + pub fn new(address: usize) -> Self { + PhysicalAddress(address) + } + + pub fn get(&self) -> usize { + self.0 + } +} + +/// A virtual address. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct VirtualAddress(usize); + +impl VirtualAddress { + pub fn new(address: usize) -> Self { + VirtualAddress(address) + } + + pub fn get(&self) -> usize { + self.0 + } +} + +/// The current memory map. It's size is maxed out to 512 entries, due to it being +/// from 0x500 to 0x5000 (800 is the absolute total) +static mut MEMORY_MAP: [MemoryArea; 512] = [MemoryArea { base_addr: 0, length: 0, _type: 0, acpi: 0 }; 512]; + +/// Memory does not exist +pub const MEMORY_AREA_NULL: u32 = 0; + +/// Memory is free to use +pub const MEMORY_AREA_FREE: u32 = 1; + +/// Memory is reserved +pub const MEMORY_AREA_RESERVED: u32 = 2; + +/// Memory is used by ACPI, and can be reclaimed +pub const MEMORY_AREA_ACPI: u32 = 3; + +/// Size of pages +pub const PAGE_SIZE: usize = 4096; + +/// A memory map area +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct MemoryArea { + pub base_addr: u64, + pub length: u64, + pub _type: u32, + pub acpi: u32 +} + +#[derive(Clone)] +pub struct MemoryAreaIter { + _type: u32, + i: usize +} + +impl MemoryAreaIter { + fn new(_type: u32) -> Self { + MemoryAreaIter { + _type: _type, + i: 0 + } + } +} + +impl Iterator for MemoryAreaIter { + type Item = &'static MemoryArea; + fn next(&mut self) -> Option { + while self.i < unsafe { MEMORY_MAP.len() } { + let entry = unsafe { &MEMORY_MAP[self.i] }; + self.i += 1; + if entry._type == self._type { + return Some(entry); + } + } + None + } +} + +static ALLOCATOR: Mutex>> = Mutex::new(None); + +/// Init memory module +/// Must be called once, and only once, +pub unsafe fn init(kernel_start: usize, kernel_end: usize) { + // Copy memory map from bootloader location + for (i, entry) in MEMORY_MAP.iter_mut().enumerate() { + *entry = *(0x500 as *const MemoryArea).offset(i as isize); + if entry._type != MEMORY_AREA_NULL { + println!("{:?}", entry); + } + } + + *ALLOCATOR.lock() = Some(RecycleAllocator::new(BumpAllocator::new(kernel_start, kernel_end, MemoryAreaIter::new(MEMORY_AREA_FREE)))); +} + +/// Init memory module after core +/// Must be called once, and only once, +pub unsafe fn init_noncore() { + if let Some(ref mut allocator) = *ALLOCATOR.lock() { + allocator.set_noncore(true) + } else { + panic!("frame allocator not initialized"); + } +} + +/// Get the number of frames available +pub fn free_frames() -> usize { + if let Some(ref allocator) = *ALLOCATOR.lock() { + allocator.free_frames() + } else { + panic!("frame allocator not initialized"); + } +} + +/// Get the number of frames used +pub fn used_frames() -> usize { + if let Some(ref allocator) = *ALLOCATOR.lock() { + allocator.used_frames() + } else { + panic!("frame allocator not initialized"); + } +} + +/// Allocate a range of frames +pub fn allocate_frames(count: usize) -> Option { + if let Some(ref mut allocator) = *ALLOCATOR.lock() { + allocator.allocate_frames(count) + } else { + panic!("frame allocator not initialized"); + } +} + +/// Deallocate a range of frames frame +pub fn deallocate_frames(frame: Frame, count: usize) { + if let Some(ref mut allocator) = *ALLOCATOR.lock() { + allocator.deallocate_frames(frame, count) + } else { + panic!("frame allocator not initialized"); + } +} + +/// A frame, allocated by the frame allocator. +/// Do not add more derives, or make anything `pub`! +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Frame { + number: usize +} + +impl Frame { + /// Get the address of this frame + pub fn start_address(&self) -> PhysicalAddress { + PhysicalAddress::new(self.number * PAGE_SIZE) + } + + //TODO: Set private + pub fn clone(&self) -> Frame { + Frame { + number: self.number + } + } + + /// Create a frame containing `address` + pub fn containing_address(address: PhysicalAddress) -> Frame { + Frame { + number: address.get() / PAGE_SIZE + } + } + + //TODO: Set private + pub fn range_inclusive(start: Frame, end: Frame) -> FrameIter { + FrameIter { + start: start, + end: end, + } + } +} + +pub struct FrameIter { + start: Frame, + end: Frame, +} + +impl Iterator for FrameIter { + type Item = Frame; + + fn next(&mut self) -> Option { + if self.start <= self.end { + let frame = self.start.clone(); + self.start.number += 1; + Some(frame) + } else { + None + } + } +} + +pub trait FrameAllocator { + fn set_noncore(&mut self, noncore: bool); + fn free_frames(&self) -> usize; + fn used_frames(&self) -> usize; + fn allocate_frames(&mut self, size: usize) -> Option; + fn deallocate_frames(&mut self, frame: Frame, size: usize); +} \ No newline at end of file diff --git a/kernel/src/memory/recycle.rs b/kernel/src/memory/recycle.rs new file mode 100644 index 0000000..b8ea6d2 --- /dev/null +++ b/kernel/src/memory/recycle.rs @@ -0,0 +1,123 @@ +//! Recycle allocator +//! Uses freed frames if possible, then uses inner allocator +//! Some code was borrowed from [Redox OS Kernel](https://github.com/redox-os/kernel) + +use alloc::Vec; + +use super::PhysicalAddress; + +use super::{Frame, FrameAllocator}; + +pub struct RecycleAllocator { + inner: T, + noncore: bool, + free: Vec<(usize, usize)>, +} + +impl RecycleAllocator { + pub fn new(inner: T) -> Self { + Self { + inner: inner, + noncore: false, + free: Vec::new(), + } + } + + fn free_count(&self) -> usize { + let mut count = 0; + for free in self.free.iter() { + count += free.1; + } + count + } + + fn merge(&mut self, address: usize, count: usize) -> bool { + for i in 0 .. self.free.len() { + let changed = { + let free = &mut self.free[i]; + if address + count * 4096 == free.0 { + free.0 = address; + free.1 += count; + true + } else if free.0 + free.1 * 4096 == address { + free.1 += count; + true + } else { + false + } + }; + + if changed { + //TODO: Use do not use recursion + let (address, count) = self.free[i]; + if self.merge(address, count) { + self.free.remove(i); + } + return true; + } + } + + false + } +} + +impl FrameAllocator for RecycleAllocator { + fn set_noncore(&mut self, noncore: bool) { + self.noncore = noncore; + } + + fn free_frames(&self) -> usize { + self.inner.free_frames() + self.free_count() + } + + fn used_frames(&self) -> usize { + self.inner.used_frames() - self.free_count() + } + + fn allocate_frames(&mut self, count: usize) -> Option { + let mut small_i = None; + { + let mut small = (0, 0); + for i in 0..self.free.len() { + let free = self.free[i]; + // Later entries can be removed faster + if free.1 >= count { + if free.1 <= small.1 || small_i.is_none() { + small_i = Some(i); + small = free; + } + } + } + } + + if let Some(i) = small_i { + let (address, remove) = { + let free = &mut self.free[i]; + free.1 -= count; + (free.0 + free.1 * 4096, free.1 == 0) + }; + + if remove { + self.free.remove(i); + } + + //println!("Restoring frame {:?}, {}", frame, count); + Some(Frame::containing_address(PhysicalAddress::new(address))) + } else { + //println!("No saved frames {}", count); + self.inner.allocate_frames(count) + } + } + + fn deallocate_frames(&mut self, frame: Frame, count: usize) { + if self.noncore { + let address = frame.start_address().get(); + if ! self.merge(address, count) { + self.free.push((address, count)); + } + } else { + //println!("Could not save frame {:?}, {}", frame, count); + self.inner.deallocate_frames(frame, count); + } + } +} \ No newline at end of file diff --git a/kernel/src/panic.rs b/kernel/src/panic.rs index 2bb1472..6dcd2aa 100755 --- a/kernel/src/panic.rs +++ b/kernel/src/panic.rs @@ -1,8 +1,7 @@ +//! Some code was borrowed from [System76 Firmware Update](https://github.com/system76/firmware-update) + use core::fmt; -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. #[lang = "eh_personality"] #[no_mangle] pub extern fn rust_eh_personality() {} @@ -10,7 +9,6 @@ pub extern fn rust_eh_personality() {} #[no_mangle] pub extern fn ___chkstk_ms() {} -// This function may be needed based on the compilation target. #[lang = "eh_unwind_resume"] #[no_mangle] pub extern fn rust_eh_unwind_resume() { @@ -28,4 +26,4 @@ pub extern fn rust_begin_panic(fmt: fmt::Arguments, file: &'static str, line: u3 #[no_mangle] pub extern fn _Unwind_Resume() { loop {} -} +} \ No newline at end of file diff --git a/kernel/x86_64-snowflake.json b/kernel/x86_64-snowflake.json index a475eba..c8d1758 100755 --- a/kernel/x86_64-snowflake.json +++ b/kernel/x86_64-snowflake.json @@ -1,17 +1,27 @@ + { - "cpu": "x86-64", - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", - "llvm-target": "x86_64-unknown-none", - "target-endian": "little", - "target-pointer-width": "64", - "target-word-size": "64", - "target-c-int-width": "32", - "os": "snowflakeos", - "arch": "x86_64", - "linker-flavor": "ld", - "pre-link-args": ["-m64"], - "no-compiler-rt": true, - "disable-redzone": true, - "eliminate-frame-pointer": false, - "morestack": false + "llvm-target": "x86_64-unknown-none", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "os": "none", + "env": "", + "vendor": "unknown", + "linker-flavor": "gcc", + "pre-link-args": ["-m64", "-nostdlib", "-static"], + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", + "dynamic-linking": false, + "executables": false, + "relocation-model": "pic", + "code-model": "kernel", + "disable-redzone": true, + "eliminate-frame-pointer": false, + "exe-suffix": "", + "has-rpath": false, + "no-compiler-rt": true, + "no-default-libraries": true, + "position-independent-executables": false, + "has-elf-tls": true } \ No newline at end of file diff --git a/share/elf.rs b/share/elf.rs index 7e9cad2..1eaff11 100755 --- a/share/elf.rs +++ b/share/elf.rs @@ -2,75 +2,96 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] -pub type Elf32_Half = u16; -pub type Elf32_Addr = u32; -pub type Elf32_Off = u32; -pub type Elf32_Sword = i32; -pub type Elf32_Word = u32; +pub type Elf64_Half = u16; +pub type Elf64_Addr = u64; +pub type Elf64_Off = u64; +pub type Elf64_Sword = i32; +pub type Elf64_Word = u32; #[repr(C)] -#[derive(Default)] +#[derive(Copy, Clone, Default)] pub struct ElfHeader { pub e_ident: [u8; 16], - pub e_object_type: Elf32_Half, - pub e_machine_type: Elf32_Half, - pub e_version: Elf32_Word, + pub e_object_type: Elf64_Half, + pub e_machine_type: Elf64_Half, + pub e_version: Elf64_Word, - pub e_entry: Elf32_Addr, - pub e_phoff: Elf32_Off, - pub e_shoff: Elf32_Off, + pub e_entry: Elf64_Addr, + pub e_phoff: Elf64_Off, + pub e_shoff: Elf64_Off, - pub e_flags: Elf32_Word, - pub e_ehsize: Elf32_Half, + pub e_flags: Elf64_Word, + pub e_ehsize: Elf64_Half, - pub e_phentsize: Elf32_Half, - pub e_phnum: Elf32_Half, + pub e_phentsize: Elf64_Half, + pub e_phnum: Elf64_Half, - pub e_shentsize: Elf32_Half, - pub e_shnum: Elf32_Half, - pub e_shstrndx: Elf32_Half, + pub e_shentsize: Elf64_Half, + pub e_shnum: Elf64_Half, + pub e_shstrndx: Elf64_Half, } + impl ElfHeader { pub fn check_header(&self) { - assert_eq!(&self.e_ident[..8], b"\x7FELF\x01\x01\x01\x00"); // Elf32, LSB, Version, Pad + assert_eq!(&self.e_ident[..8], b"\x7FELF\x02\x01\x01\x00"); // Elf64, LSB, Version, Pad assert_eq!(self.e_version, 1); } } + #[repr(C)] -#[derive(Copy,Clone,Default)] +#[derive(Copy, Clone, Debug, Default)] pub struct PhEnt { - pub p_type: Elf32_Word, - pub p_offset: Elf32_Off, - pub p_vaddr: Elf32_Addr, - pub p_paddr: Elf32_Addr, // aka load - pub p_filesz: Elf32_Word, - pub p_memsz: Elf32_Word, - pub p_flags: Elf32_Word, - pub p_align: Elf32_Word, + pub p_type: Elf64_Word, + pub p_flags: Elf64_Word, + pub p_offset: Elf64_Off, + pub p_vaddr: Elf64_Addr, + pub p_paddr: Elf64_Addr, // aka load + pub p_filesz: Elf64_Addr, + pub p_memsz: Elf64_Addr, + pub p_align: Elf64_Addr, +} + +impl PhEnt { + pub fn start_address(&self) -> usize { + self.p_paddr as usize + } + + pub fn end_address(&self) -> usize { + (self.p_paddr + self.p_memsz) as usize + } + + pub fn flags(&self) -> ElfSectionFlags { + ElfSectionFlags::from_bits_truncate(self.p_flags.into()) + } + + pub fn is_allocated(&self) -> bool { + self.flags().contains(ElfSectionFlags::ELF_SECTION_ALLOCATED) + } } + #[repr(C)] #[derive(Copy,Clone)] pub struct ShEnt { - sh_name: Elf32_Word, - sh_type: Elf32_Word, - sh_flags: Elf32_Word, - sh_addr: Elf32_Addr, - sh_offset: Elf32_Off, - sh_size: Elf32_Word, - sh_link: Elf32_Word, - sh_info: Elf32_Word, - sh_addralign: Elf32_Word, - sh_entsize: Elf32_Word, + sh_name: Elf64_Word, + sh_type: Elf64_Word, + sh_flags: Elf64_Word, + sh_addr: Elf64_Addr, + sh_offset: Elf64_Off, + sh_size: Elf64_Word, + sh_link: Elf64_Word, + sh_info: Elf64_Word, + sh_addralign: Elf64_Word, + sh_entsize: Elf64_Word, } - -pub struct ElfFile(ElfHeader); +#[derive(Copy,Clone)] +pub struct ElfFile(pub ElfHeader); impl ElfFile { pub fn check_header(&self) { self.0.check_header(); } - fn phents(&self) -> PhEntIter { + pub fn phents(&self) -> PhEntIter { assert_eq!( self.0.e_phentsize as usize, ::core::mem::size_of::() ); // SAFE: Assuming the file is correct... let slice: &[PhEnt] = unsafe { @@ -93,7 +114,10 @@ impl ElfFile self.0.e_entry as usize } } -struct PhEntIter<'a>(&'a [PhEnt]); + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub struct PhEntIter<'a>(pub &'a [PhEnt]); impl<'a> Iterator for PhEntIter<'a> { type Item = PhEnt; fn next(&mut self) -> Option { @@ -241,4 +265,14 @@ pub extern "C" fn elf_load_symbols(file_base: &ElfFile, output: &mut SymbolInfo) println!("- output = {:?}", output); pos as u32 +} + +bitflags! { + pub struct ElfSectionFlags: u64 { + const ELF_SECTION_WRITABLE = 0x1; + const ELF_SECTION_ALLOCATED = 0x2; + const ELF_SECTION_EXECUTABLE = 0x4; + // plus environment-specific use at 0x0F000000 + // plus processor-specific use at 0xF0000000 + } } \ No newline at end of file diff --git a/share/uefi_proto.rs b/share/uefi_proto.rs index 355b88c..da11715 100755 --- a/share/uefi_proto.rs +++ b/share/uefi_proto.rs @@ -1,5 +1,7 @@ mod color; +mod elf; use color::*; +use elf::PhEntIter; #[repr(C)] pub struct Info { @@ -8,9 +10,15 @@ pub struct Info { pub cmdline_ptr: *const u8, pub cmdline_len: usize, + pub elf_sections: Option>, + pub kernel_base: usize, + pub kernel_size: usize, + pub stack_base: usize, + pub stack_size: usize, + pub map_addr: u64, - pub map_entnum: u32, - pub map_entsz: u32, + pub map_len: u32, + pub descriptor_size: u32, pub video_info: *const VideoInfo, }