diff --git a/kernel-rs/.gitignore b/kernel-rs/.gitignore
new file mode 100644
index 00000000..dd39fc22
--- /dev/null
+++ b/kernel-rs/.gitignore
@@ -0,0 +1,3 @@
+target
+build
+Cargo.lock
diff --git a/kernel-rs/.gitmodules b/kernel-rs/.gitmodules
new file mode 100644
index 00000000..6b9b26f1
--- /dev/null
+++ b/kernel-rs/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "multiboot2-elf64"]
+ path = multiboot2-elf64
+ url = https://github.com/jzck/multiboot2-elf64.git
+[submodule "x86"]
+ path = x86
+ url = https://github.com/jzck/x86.git
diff --git a/kernel-rs/.travis.yml b/kernel-rs/.travis.yml
new file mode 100644
index 00000000..462f1d9d
--- /dev/null
+++ b/kernel-rs/.travis.yml
@@ -0,0 +1,18 @@
+language: rust
+before_install:
+ - sudo apt-get update
+ - sudo apt-get install -y nasm build-essential gcc
+ - cargo install xargo
+ - rustup override add nightly
+ - rustup component add rust-src
+script:
+ - make
+after_success: |
+ cargo doc &&
+ echo "" > target/doc/index.html &&
+ sudo pip install ghp-import &&
+ ghp-import -n target/doc &&
+ git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
+env:
+ global:
+ secure: KugnyzyUuT4x292BIJoXYBcHbGb1o8961gzrmQzdxCkn0sjg+UI9WUmQj2rZqxxRXCHK9VOaT/vYk9oYXoRv3cISDmsQbJpisUapmil8u3NWn2UVU6oePQrdj2+gF+hy7/K8NeEFE2X8Gv8MX6lsv7NZLTiN67UEpkuxWTrX4RKHDvIQDonUlI+56vgY3hvl4/kI6mE+BSVw9RPK8OWD1T0wwMzAjTCKERonAwI846HDNadWjXRq0+j+n84BJekxICAQRquO2MM94j+MQgCIV0ZQsa9bpG7CfvgLVXLh3NWjEYwkmWFz6hyxLhY4XnG9MNlBh+dN391tDuRoRUlsoI7P1Ym10aYAz1uEEfTQKemVWXOI8DqZDJL0MO3y9+LePb9hTjJ9GOoQ7q9v6v1l9MzWYCy4KBv4QWDEpIKsa7WM/ExfqlZ7MuVW6rzTZmmCScElSk295Z6aH993FrzsyREP16Ch2RniewA598lwinahYR8eYXKjkasi1WTJcLMvQN9nnUe0vhFUFNXPDQPLeX7ZEvzvvcOBY7kZG5zHUeXzIWq7D7tXZTqu/48cgwTvjQ5Q/FARXoffd+RaX/nZInVMdeI7phruOEawUNPXSEdNjNxIedi4PPnIRXT+DgstQeBSKrgl7HYNwnFC3Q+NfoU7oWoyivUtcQ1xZZj03f8=
diff --git a/kernel-rs/Cargo.toml b/kernel-rs/Cargo.toml
new file mode 100644
index 00000000..293bdd48
--- /dev/null
+++ b/kernel-rs/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "bluesnow"
+version = "0.1.0"
+authors = ["Jack Halford ", "William Escande "]
+
+[lib]
+crate-type = ["staticlib"]
+
+[dependencies]
+rlibc = "1.0"
+bitflags = "1.0.1"
+spin = "0.4"
+slab_allocator = "0.3.5"
+x86 = { path = "x86" } # forked for IA-32 compat
+multiboot2 = { path = "multiboot2-elf64" } # forked to add rsdp tag
+
+[dependencies.raw-cpuid]
+# need to use github/master because of features not yet on crates.io
+git = "https://github.com/gz/rust-cpuid"
+features = ["nightly"]
+
+[dependencies.lazy_static]
+version = "1.0.0"
+features = ["spin_no_std"]
+
+#[dependencies.compiler_builtins]
+##I'm waiting for somebody to port i386/udivdi3.S ... :(((
+#git = "https://github.com/rust-lang-nursery/compiler-builtins"
diff --git a/kernel-rs/Makefile b/kernel-rs/Makefile
new file mode 100644
index 00000000..cf398440
--- /dev/null
+++ b/kernel-rs/Makefile
@@ -0,0 +1,46 @@
+SHELL := /bin/bash
+
+ARCH := x86
+OS := bluesnow
+TARGET ?= $(ARCH)-$(OS)
+
+all:
+ @printf "make kernel\t# build kernel binary\n"
+ @printf "make iso\t# build iso cdrom\n"
+ @printf "make qemu\t# run iso in qemu\n"
+
+## COMPILE ASM (nasm)
+asm_source := $(wildcard src/arch/$(ARCH)/*.asm)
+asm_object := $(patsubst src/arch/$(ARCH)/%.asm, build/arch/$(ARCH)/%.o, $(asm_source))
+NASM := /usr/bin/nasm -f elf -gdwarf
+build/arch/$(ARCH)/%.o: src/arch/$(ARCH)/%.asm Makefile
+ @mkdir -p $(shell dirname $@)
+ $(NASM) $< -o $@
+
+## COMPILE RUST (xargo)
+rust_os := target/$(TARGET)/debug/lib$(OS).a
+$(rust_os): $(TARGET).json Makefile
+ TERM=xterm RUST_TARGET_PATH="$(shell pwd)" xargo build --target $(TARGET)
+
+## LINKAGE
+KERNEL := build/$(OS)-$(ARCH).bin
+linker_script := src/arch/$(ARCH)/linker.ld
+LD := /usr/bin/ld -m elf_i386 -L ./ -n --gc-sections
+$(KERNEL): $(rust_os) $(asm_object) $(linker_script) Makefile
+ $(LD) -o $@ -T $(linker_script) $(asm_object) $(rust_os)
+
+clean:
+ xargo clean
+ rm -rf build
+
+.PHONY: clean kernel iso $(rust_os)
+
+# Bootloader recipes
+ISO := $(KERNEL:.bin=.iso)
+iso: $(ISO)
+include mk/grub.mk
+
+# Emulation recipes
+include mk/qemu.mk
+
+kernel: $(KERNEL)
diff --git a/kernel-rs/README.md b/kernel-rs/README.md
new file mode 100644
index 00000000..cb0340cc
--- /dev/null
+++ b/kernel-rs/README.md
@@ -0,0 +1,48 @@
+# !! Switched to ziglang -> [new repo here](https://github.com/jzck/kernel-zig) !!
+
+Kernel from scratch (KFS) series of projects at Ecole 42 !
+
+### [documentation](https://jzck.github.io/kernel/bluesnow/index.html)
+
+# building
+
+`git submodule udpate --init`
+
+ - `nasm` compiles the bootcode
+ - `ld` links the bootcode and rust binary
+ - `grub-mkrescue` builds the iso (need xorriso and mtools)
+ - `xargo` builds rust code
+
+See `.travis.yml` to get an ubuntu environment ready
+on archlinux `pacman -S rustup make grub xorriso mtools binutils gcc qemu`
+on voidlinux `xbps-install -S rustup make grub xorriso mtools binutils gcc qemu nasm`
+
+
+#### rust setup
+
+We build on nightly channel because of some cool features not yet in stable.
+We need the rust sources to build with xargo for cross-compiling to custom platform.
+
+```
+rustup component add rust-src
+rustup override add nightly
+rustup default nightly
+cargo install xargo
+```
+
+# running
+
+ - `make iso` builds a bootable iso with grub
+ - `make qemu` runs the iso,
+ - `make qemu-reload` reloads the CD
+
+# todo
+
+ - remove assembly for a pure rust entry point
+ - replace grub with something lighter (no bootloader at all with `qemu -kernel` ?)
+
+# inspiration
+
+ - [wiki.osdev.org](https://wiki.osdev.org) is a fucking goldmine
+ - [Phil Opperman's "Writing an OS in rust"](https://os.phil-opp.com/)
+ - [Redox kernel](https://github.com/redox/kernel)
diff --git a/kernel-rs/Xargo.toml b/kernel-rs/Xargo.toml
new file mode 100644
index 00000000..91b4c5ff
--- /dev/null
+++ b/kernel-rs/Xargo.toml
@@ -0,0 +1,2 @@
+[target.x86-bluesnow.dependencies]
+alloc = {}
diff --git a/kernel-rs/mk/grub.mk b/kernel-rs/mk/grub.mk
new file mode 100644
index 00000000..78240426
--- /dev/null
+++ b/kernel-rs/mk/grub.mk
@@ -0,0 +1,8 @@
+grub-cfg := src/arch/$(ARCH)/grub.cfg
+isodir := build/isofiles
+
+$(ISO): $(KERNEL) $(grub-cfg) Makefile
+ @mkdir -p $(isodir)/boot/grub
+ @cp $(grub-cfg) $(isodir)/boot/grub
+ @cp $(KERNEL) $(isodir)/boot/$(OS)
+ grub-mkrescue -o $@ $(isodir) 2>/dev/null
diff --git a/kernel-rs/mk/qemu.mk b/kernel-rs/mk/qemu.mk
new file mode 100644
index 00000000..014477be
--- /dev/null
+++ b/kernel-rs/mk/qemu.mk
@@ -0,0 +1,27 @@
+QEMU_SOCKET := /tmp/qemu.sock
+QEMU_MONITOR := socat - unix-connect:$(QEMU_SOCKET)
+QEMU_GDB_PORT := 4242
+
+qemu:
+ qemu-system-i386\
+ -cdrom $(ISO)\
+ -S\
+ -enable-kvm\
+ -curses\
+ -gdb tcp::$(QEMU_GDB_PORT)\
+ -monitor unix:${QEMU_SOCKET},server,nowait\
+ -drive file=disk,if=ide,index=1
+
+qemu-gdb:
+ gdb\
+ -q\
+ -symbols "$(KERNEL)" \
+ -ex "target remote :$(QEMU_GDB_PORT)"\
+ -ex "set arch i386"
+
+qemu-monitor:
+ $(QEMU_MONITOR)
+qemu-reload:
+ echo "stop" | $(QEMU_MONITOR) &>/dev/null
+ echo "change ide1-cd0 $(ISO)" | $(QEMU_MONITOR) &>/dev/null
+ echo "system_reset" | $(QEMU_MONITOR) &>/dev/null
diff --git a/kernel-rs/multiboot2-elf64 b/kernel-rs/multiboot2-elf64
new file mode 160000
index 00000000..32552947
--- /dev/null
+++ b/kernel-rs/multiboot2-elf64
@@ -0,0 +1 @@
+Subproject commit 32552947aa30414a96d9a8e291aceaf18320b3ef
diff --git a/kernel-rs/src/acpi/dsdt.rs b/kernel-rs/src/acpi/dsdt.rs
new file mode 100644
index 00000000..b93d407e
--- /dev/null
+++ b/kernel-rs/src/acpi/dsdt.rs
@@ -0,0 +1,98 @@
+use super::{check_signature, ACPISDTHeader};
+use core::mem;
+use x86::devices::io::{Io, Pio};
+
+static mut DSDT: DSDT = DSDT {
+ valid: false,
+ dsdt: None,
+ s5_ptr: 0,
+ slp_typ_a: 0,
+ slp_typ_b: 0,
+};
+
+struct DSDT {
+ valid: bool,
+ dsdt: Option<&'static ACPISDTHeader>,
+ s5_ptr: u32,
+ slp_typ_a: u16,
+ slp_typ_b: u16,
+}
+
+impl DSDT {
+ fn init(&mut self, addr: u32) -> Result<(), &'static str> {
+ self.dsdt = Some(unsafe { &(*(addr as *const ACPISDTHeader)) });
+ self.s5_ptr = self.find_s5(addr)?;
+ self.parse_s5();
+ self.valid = true;
+ Ok(())
+ }
+
+ fn find_s5(&self, addr: u32) -> Result {
+ let dsdt_start = addr + mem::size_of::() as u32;
+ let dsdt_end = dsdt_start + self.dsdt.unwrap().length;
+ for addr in dsdt_start..dsdt_end {
+ if check_signature(addr, "_S5_") {
+ if (check_signature(addr - 1, "\x08") || check_signature(addr - 2, "\x08\\"))
+ && check_signature(addr + 4, "\x12")
+ {
+ return Ok(addr);
+ }
+ }
+ }
+ Err("Can not find S5 section in DSDT")
+ }
+
+ fn parse_s5(&mut self) {
+ let ptr = self.s5_ptr + 5;
+ let ptr = ((unsafe { *(ptr as *const u8) } & 0xC0) >> 6) + 2;
+ let ptr = if unsafe { *(ptr as *const u8) } == 0x0A {
+ ptr + 1
+ } else {
+ ptr
+ }; // Skip bytePrefix
+ self.slp_typ_a = (unsafe { *(ptr as *const u8) } as u16) << 10;
+ let ptr = ptr + 1;
+ let ptr = if unsafe { *(ptr as *const u8) } == 0x0A {
+ ptr + 1
+ } else {
+ ptr
+ }; // Skip bytePrefix
+ self.slp_typ_b = (unsafe { *(ptr as *const u8) } as u16) << 10;
+ }
+}
+
+fn is_init() -> Result<(), &'static str> {
+ match unsafe { DSDT.valid } {
+ true => Ok(()),
+ false => match unsafe { DSDT.dsdt } {
+ Some(_) => Err("Differentiated System Description Pointer (DSDP) is not valid"),
+ None => Err("Differentiated System Description Pointer (DSDP) is not initialized"),
+ },
+ }
+}
+
+/// ## Initialize Differentiated System Description Table (DSDT)
+/// input param addr is contain in FADT
+pub fn init(addr: u32) -> Result<(), &'static str> {
+ if ACPISDTHeader::valid(addr, "DSDT") {
+ return unsafe { DSDT.init(addr) };
+ }
+ return Err("Can not find Differentiated System Description Table (DSDT).");
+}
+
+/// NOT COMPATIBLE WITH VIRTUALBOX
+/// Send shutdown signal
+/// outw(PM1a_CNT_BLK, SLP_TYPx | SLP_EN)
+pub fn shutdown(pm1_cnt: [u16; 2]) -> Result<(), &'static str> {
+ is_init()?;
+ let slp_typ = unsafe { DSDT.slp_typ_a } | (1 << 13);
+ let mut pin: Pio = Pio::new(pm1_cnt[0]);
+ pin.write(slp_typ);
+ if pm1_cnt[1] != 0 {
+ let slp_typ = unsafe { DSDT.slp_typ_b } | (1 << 13);
+ let mut pin: Pio = Pio::new(pm1_cnt[1]);
+ pin.write(slp_typ);
+ // cpuio::outw(pm1_cnt[1], slp_typ);
+ }
+ Ok(())
+}
diff --git a/kernel-rs/src/acpi/fadt.rs b/kernel-rs/src/acpi/fadt.rs
new file mode 100644
index 00000000..95f7c244
--- /dev/null
+++ b/kernel-rs/src/acpi/fadt.rs
@@ -0,0 +1,165 @@
+use super::{ACPISDTHeader, ACPISDTIter};
+use x86::devices::io::{Io, Pio};
+
+#[repr(C)]
+#[derive(Debug, Clone)]
+struct GenericAddressStructure {
+ addressspace: u8,
+ bitwidth: u8,
+ bitoffset: u8,
+ accesssize: u8,
+ noused: u32,
+ address: u64,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone)]
+struct FADT {
+ header: ACPISDTHeader,
+ firmwarectrl: u32,
+ dsdt: u32,
+
+ // field used in acpi 1.0; no longer in use, for compatibility only
+ reserved: u8,
+
+ preferredpowermanagementprofile: u8,
+ sci_interrupt: u16,
+ smi_commandport: u32,
+ acpi_enable: u8,
+ acpidisable: u8,
+ s4bios_req: u8, //no use
+ pstate_control: u8, //no use
+ pm1aeventblock: u32, //no use
+ pm1beventblock: u32, //no use
+ pm1acontrolblock: u32,
+ pm1bcontrolblock: u32,
+ pm2controlblock: u32, //no use
+ pmtimerblock: u32, //no use
+ gpe0block: u32, //no use
+ gpe1block: u32, //no use
+ pm1eventlength: u8, //no use
+ pm1controllength: u8,
+ pm2controllength: u8, //no use
+ pmtimerlength: u8, //no use
+ gpe0length: u8, //no use
+ gpe1length: u8, //no use
+ gpe1base: u8, //no use
+ cstatecontrol: u8, //no use
+ worstc2latency: u16, //no use
+ worstc3latency: u16, //no use
+ flushsize: u16, //no use
+ flushstride: u16, //no use
+ dutyoffset: u8, //no use
+ dutywidth: u8, //no use
+ dayalarm: u8, //no use
+ monthalarm: u8, //no use
+ century: u8, //no use
+
+ // reserved in acpi 1.0; used since acpi 2.0+
+ bootarchitectureflags: u16,
+
+ reserved2: u8,
+ flags: u32,
+
+ // 12 byte structure; see below for details
+ resetreg: GenericAddressStructure,
+
+ resetvalue: u8,
+ reserved3: [u8; 3],
+
+ // 64bit pointers - Available on ACPI 2.0+
+ x_firmwarecontrol: u64,
+ x_dsdt: u64,
+
+ x_pm1aeventblock: GenericAddressStructure,
+ x_pm1beventblock: GenericAddressStructure,
+ x_pm1acontrolblock: GenericAddressStructure,
+ x_pm1bcontrolblock: GenericAddressStructure,
+ x_pm2controlblock: GenericAddressStructure,
+ x_pmtimerblock: GenericAddressStructure,
+ x_gpe0block: GenericAddressStructure,
+ x_gpe1block: GenericAddressStructure,
+}
+
+static mut FADT: Option = None;
+
+/// ## Initialize Fixed ACPI Description Table (FADT)
+/// input param addr is contain in other ptr of rsdt
+pub fn init(sdt_iter: ACPISDTIter) -> Result<(), &'static str> {
+ for sdt_ptr in sdt_iter {
+ if ACPISDTHeader::valid(sdt_ptr, "FACP") {
+ // Where is "FADT"? Shut up is magic
+ let fadt_tmp: FADT = unsafe { (*(sdt_ptr as *const FADT)).clone() };
+ unsafe { FADT = Some(fadt_tmp.clone()) };
+ if !is_enable()? {
+ // TODO do i have to check if enabled before init ???
+ let smi_cmd = fadt_tmp.smi_commandport as u16; // TODO WHY DO I NEED THIS FUCKING CAST
+ let acpi_enable = fadt_tmp.acpi_enable;
+ //TODO not sexy it !
+ let mut pin: Pio = Pio::new(smi_cmd);
+ pin.write(acpi_enable);
+ // cpuio::outb(smi_cmd, acpi_enable); // send acpi enable command
+ }
+ return Ok(());
+ }
+ }
+ return Err("Can not find Fixed ACPI Description Table (FADT).");
+}
+
+fn is_init() -> Result {
+ match unsafe { FADT.clone() } {
+ Some(fadt) => Ok(fadt),
+ None => Err("Fixed ACPI Description Table (FADT) is not initialized"),
+ }
+}
+
+/// Return Dsdt address
+/// FADT must have been initialized first
+pub fn dsdtaddr() -> Result {
+ let fadt = is_init()?;
+ return Ok(fadt.dsdt);
+}
+
+fn get_cnt(fadt: FADT) -> [u16; 2] {
+ [fadt.pm1acontrolblock as u16, fadt.pm1bcontrolblock as u16] // TODO WHY DO I NEED THIS FUCKING CAST
+}
+
+/// Return true/false depending of acpi is enable
+pub fn is_enable() -> Result {
+ let fadt = is_init()?;
+ let pm1_cnt = get_cnt(fadt);
+ let pin: Pio = Pio::new(pm1_cnt[0]);
+ if pm1_cnt[1] == 0 {
+ Ok(pin.read() & 0x1 == 0x1)
+ // Ok(cpuio::inw(pm1_cnt[0]) & 0x1 == 0x1)
+ } else {
+ let pin2: Pio = Pio::new(pm1_cnt[1]);
+ Ok(pin.read() & 0x1 == 0x1 || pin2.read() & 0x1 == 0x1)
+ // Ok(cpuio::inw(pm1_cnt[0]) & 0x1 == 0x1 || cpuio::inw(pm1_cnt[1]) & 0x1 == 0x1)
+ }
+}
+
+/// Return a array with [pm1a, pm1b]
+/// FADT must have been initialized first
+pub fn get_controlblock() -> Result<[u16; 2], &'static str> {
+ if !is_enable()? {
+ Err("ACPI is not enabled")
+ } else {
+ Ok(get_cnt(is_init()?)) // TODO redondant call to is_init
+ }
+}
+pub fn reboot() -> Result<(), &'static str> {
+ if !is_enable()? {
+ Err("ACPI is not enabled")
+ } else {
+ let fadt = is_init()?;
+ println!(
+ "fadt on {} ({}), value is {}",
+ fadt.resetreg.address as u32, fadt.resetreg.address as u16, fadt.resetvalue
+ );
+ let mut pin: Pio = Pio::new(fadt.resetreg.address as u16);
+ pin.write(fadt.resetvalue);
+ // cpuio::outb(fadt.resetreg.address as u16, fadt.resetvalue); //TODO do it work
+ Ok(())
+ }
+}
diff --git a/kernel-rs/src/acpi/mod.rs b/kernel-rs/src/acpi/mod.rs
new file mode 100644
index 00000000..58882099
--- /dev/null
+++ b/kernel-rs/src/acpi/mod.rs
@@ -0,0 +1,167 @@
+mod rsdp;
+mod rsdt;
+mod xsdt;
+mod fadt;
+mod dsdt;
+
+use core;
+use core::mem;
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+struct ACPISDTHeader {
+ signature: [u8; 4],
+ length: u32,
+ revision: u8,
+ checksum: u8,
+ oemid: [u8; 6],
+ oemtableid: [u8; 8],
+ oemrevision: u32,
+ creatorid: u32,
+ creatorrevision: u32,
+}
+
+impl ACPISDTHeader {
+ pub fn valid(addr: u32, signature: &str) -> bool {
+ if check_signature(addr, signature) {
+ let ptr_tmp = addr as *const ACPISDTHeader;
+ if check_checksum(addr, unsafe { (*ptr_tmp).length } as usize) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+static mut ACPI: Acpi = Acpi {
+ valid: false,
+ v2: false,
+};
+
+struct Acpi {
+ valid: bool,
+ v2: bool,
+}
+
+impl Acpi {
+ fn common_init(&mut self) -> Result<(), &'static str> {
+ if self.v2 {
+ // Xsdt Address:
+ // 64-bit physical address of the XSDT table. If you detect ACPI Version 2.0 you should use this table instead of RSDT even on x86, casting the address to uint32_t.
+ xsdt::init(rsdp::xsdtaddr()?)?;
+ fadt::init(xsdt::iter()?)?;
+ } else {
+ rsdt::init(rsdp::rsdtaddr()?)?;
+ fadt::init(rsdt::iter()?)?;
+ }
+ dsdt::init(fadt::dsdtaddr()?)?;
+ self.valid = true;
+ Ok(())
+ }
+ fn init(&mut self) -> Result<(), &'static str> {
+ self.v2 = rsdp::init()?;
+ self.common_init()
+ }
+ fn load(&mut self, rsdp_addr: u32) -> Result<(), &'static str> {
+ self.v2 = rsdp::load(rsdp_addr)?;
+ self.common_init()
+ }
+}
+
+fn check_signature(addr: u32, id: &str) -> bool {
+ let signature = match core::str::from_utf8(unsafe {
+ core::slice::from_raw_parts_mut(addr as *mut u8, id.len())
+ }) {
+ Ok(y) => y,
+ Err(_) => return false,
+ };
+ return signature == id;
+}
+
+fn check_checksum(addr: u32, len: usize) -> bool {
+ let byte_array = unsafe { core::slice::from_raw_parts_mut(addr as *mut u8, len) };
+ let mut sum: u32 = 0;
+ for byte in byte_array {
+ sum += *byte as u32;
+ }
+ return sum as u8 == 0;
+}
+
+pub struct ACPISDTIter {
+ pos: usize,
+ width: usize,
+ sdt: u32,
+ len: usize,
+}
+
+impl ACPISDTIter {
+ fn new(
+ acpi_sdt: Option<*const ACPISDTHeader>,
+ ptr_len: usize,
+ ) -> Result {
+ match acpi_sdt {
+ None => Err("There is no ACPI System Description Table (ACPISDTHeader) to iter on."),
+ Some(ptr) => Ok(ACPISDTIter {
+ pos: 0,
+ width: ptr_len,
+ sdt: ptr as u32 + mem::size_of::() as u32,
+ len: (unsafe { (*ptr).length } as usize - mem::size_of::())
+ / ptr_len,
+ }),
+ }
+ }
+}
+
+impl Iterator for ACPISDTIter {
+ type Item = u32;
+
+ fn next(&mut self) -> Option {
+ self.pos += 1;
+ if self.pos > self.len {
+ return None;
+ }
+ let ret = Some(unsafe { *(self.sdt as *const u32) });
+ self.sdt += self.width as u32;
+ return ret;
+ }
+}
+
+/// Initalized the ACPI module
+pub fn init() -> Result<(), &'static str> {
+ if unsafe{ACPI.valid} { return Ok(()); }
+ unsafe { ACPI.init() }
+}
+
+/// Load the ACPI module, addr given is a ptr to RSDP
+pub fn load(rsdp_addr: u32) -> Result<(), &'static str> {
+ if unsafe{!ACPI.valid} { return Ok(()); }
+ unsafe { ACPI.load(rsdp_addr) }
+}
+
+/// Proceed to ACPI shutdown
+/// This function doesn't work with Virtual Box yet
+pub fn shutdown() {
+ if unsafe{!ACPI.valid} { return; }
+ dsdt::shutdown(fadt::get_controlblock().unwrap()).unwrap();
+}
+
+/// Proceed to ACPI reboot
+/// This function need ACPI in v2
+pub fn reboot() {
+ if unsafe {!ACPI.valid} { println!("ACPI not initialized"); }
+ if unsafe { ACPI.v2 } {
+ fadt::reboot().unwrap()
+ } else {
+ println!("ACPI reboot only available in ACPI v2+");
+ }
+}
+
+/// Display state of ACPI
+pub fn info() {
+ if unsafe { !ACPI.valid } { println!("ACPI not initialized"); return }
+ match fadt::is_enable() {
+ Ok(true) => println!("ACPI is disabled"),
+ Ok(false) => println!("ACPI is disabled"),
+ Err(msg) => println!("error while checking ACPI: {}", msg)
+ }
+}
diff --git a/kernel-rs/src/acpi/rsdp.rs b/kernel-rs/src/acpi/rsdp.rs
new file mode 100644
index 00000000..028f1451
--- /dev/null
+++ b/kernel-rs/src/acpi/rsdp.rs
@@ -0,0 +1,85 @@
+use super::{check_checksum, check_signature};
+use core::mem;
+
+#[repr(C)]
+#[derive(Clone)]
+struct RSDP {
+ signature: [u8; 8],
+ checksum: u8,
+ oemid: [u8; 6],
+ revision: u8,
+ rsdtaddr: u32,
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct RSDP20 {
+ rsdp: RSDP,
+ length: u32,
+ xsdtaddress: u64,
+ extendedchecksum: u8,
+ reserved: [u8; 3],
+}
+
+static mut RSDPTR: Option = None;
+
+/// RSDP load will check is RSDP is present at the addr sent.
+/// Return a bool
+/// true => RSDP is V2
+/// false => RSDP is V1
+pub fn load(addr: u32) -> Result {
+ if check_signature(addr, "RSD PTR ") {
+ let rsdp_tmp: RSDP20 = unsafe { (*(addr as *const RSDP20)).clone() };
+ let revision = rsdp_tmp.rsdp.revision;
+ if (revision == 0 && check_checksum(addr, mem::size_of::()))
+ || (revision == 2 && check_checksum(addr, mem::size_of::()))
+ {
+ unsafe { RSDPTR = Some(rsdp_tmp) };
+ return Ok(revision == 2);
+ }
+ }
+ Err("Not a valid RSD ptr")
+}
+
+fn memory_finding() -> Result {
+ let mut i = 0;
+ while i < 0x1000000 {
+ i += 8;
+ if let Ok(result) = load(i) {
+ return Ok(result);
+ }
+ }
+ Err("Can not find Root System Description Pointer (RSDP).")
+}
+
+fn is_init() -> Result {
+ match unsafe { RSDPTR.clone() } {
+ Some(rsdptr) => Ok(rsdptr),
+ None => Err("Root System Description Pointer (RSDP) is not initialized"),
+ }
+}
+
+/// Return a ptr on xsdt
+/// RSDP must have been initialized first
+pub fn xsdtaddr() -> Result {
+ let rsdptr = is_init()?;
+ let revision = rsdptr.rsdp.revision;
+ if revision != 2 {
+ return Err("Wrong RSDP version asked");
+ }
+ return Ok(rsdptr.xsdtaddress);
+}
+
+/// Return a ptr on rsdt
+/// RSDP must have been initialized first
+pub fn rsdtaddr() -> Result {
+ let rsdptr = is_init()?;
+ return Ok(rsdptr.rsdp.rsdtaddr);
+}
+
+/// RSDP init will iter on addr in [0x0 - 0x1000000] to find "RSDP PTR "
+/// if you already know the location, you should prefer to use load function
+/// return an Error if there is no RSDP in memory, or return the value of load function
+pub fn init() -> Result {
+ memory_finding()
+}
diff --git a/kernel-rs/src/acpi/rsdt.rs b/kernel-rs/src/acpi/rsdt.rs
new file mode 100644
index 00000000..59ed0fb0
--- /dev/null
+++ b/kernel-rs/src/acpi/rsdt.rs
@@ -0,0 +1,20 @@
+use super::{ACPISDTHeader, ACPISDTIter};
+
+//TODO this can work only if pagging is disabled
+static mut RSDT: Option<*const ACPISDTHeader> = None;
+
+/// ## Initialize Root System Description Table (RSDT)
+/// input param addr is contain in RSDP
+pub fn init(addr: u32) -> Result<(), &'static str> {
+ if ACPISDTHeader::valid(addr, "RSDT") {
+ unsafe { RSDT = Some(addr as *const ACPISDTHeader) };
+ return Ok(());
+ }
+ return Err("Can not find Root System Description Table (RSDT).");
+}
+
+/// Return a iterable of ptr contained in RSDT
+/// RSDT must have been initialized first
+pub fn iter() -> Result {
+ ACPISDTIter::new(unsafe { RSDT }, 4)
+}
diff --git a/kernel-rs/src/acpi/xsdt.rs b/kernel-rs/src/acpi/xsdt.rs
new file mode 100644
index 00000000..eb05c1ae
--- /dev/null
+++ b/kernel-rs/src/acpi/xsdt.rs
@@ -0,0 +1,22 @@
+use super::{ACPISDTHeader, ACPISDTIter};
+
+//TODO this can work only if pagging is disabled
+static mut XSDT: Option<*const ACPISDTHeader> = None;
+
+/// ## Initialize Root System Description Table (XSDT)
+/// input param addr is contain in RSDP
+pub fn init(addr: u64) -> Result<(), &'static str> {
+ assert!((addr as u32) as u64 == addr);
+ let addr: u32 = addr as u32;
+ if ACPISDTHeader::valid(addr, "XSDT") {
+ unsafe { XSDT = Some(addr as *const ACPISDTHeader) };
+ return Ok(());
+ }
+ return Err("Can not find eXtended System Descriptor Table (XSDT).");
+}
+
+/// Return a iterable of ptr contained in XSDT
+/// XSDT must have been initialized first
+pub fn iter() -> Result {
+ ACPISDTIter::new(unsafe { XSDT }, 8)
+}
diff --git a/kernel-rs/src/allocator/mod.rs b/kernel-rs/src/allocator/mod.rs
new file mode 100644
index 00000000..6c5fb136
--- /dev/null
+++ b/kernel-rs/src/allocator/mod.rs
@@ -0,0 +1,27 @@
+// pub use self::ALLOCATOR;
+
+use x86::structures::paging::*;
+use arch::x86::paging::*;
+
+fn map_heap(active_table: &mut ActivePageTable) {
+ //zone for heap is predefined in `consts.rs`
+ for page in ::KERNEL_HEAP_RANGE {
+ active_table.map(page, PageTableFlags::WRITABLE);
+ }
+}
+
+/// should be called only once
+pub unsafe fn init(active_table: &mut ActivePageTable) {
+ let offset = ::KERNEL_HEAP_OFFSET;
+ let size = ::KERNEL_HEAP_SIZE;
+
+ map_heap(active_table);
+
+ //slab allocator
+ super::ALLOCATOR.init(offset.as_u32() as usize, size as usize);
+}
+
+#[alloc_error_handler]
+fn foo(_: core::alloc::Layout) -> ! {
+ panic!("alloc_error_handler");
+}
diff --git a/kernel-rs/src/arch/mod.rs b/kernel-rs/src/arch/mod.rs
new file mode 100644
index 00000000..baf9581d
--- /dev/null
+++ b/kernel-rs/src/arch/mod.rs
@@ -0,0 +1,4 @@
+// we only support a single architecture at the moment
+// we call it x86 but we only support all 32bit
+// intel architectures (IA-32) such as i[3456]86
+pub mod x86;
diff --git a/kernel-rs/src/arch/x86/boot.asm b/kernel-rs/src/arch/x86/boot.asm
new file mode 100644
index 00000000..892fc549
--- /dev/null
+++ b/kernel-rs/src/arch/x86/boot.asm
@@ -0,0 +1,98 @@
+global start
+extern x86_start
+
+section .text
+bits 32
+start:
+
+ ; our stack, located in bss, linker.ld puts bss at the end of the binary
+ mov esp, stack_top
+
+ ; multiboot information pointer
+ push ebx
+
+ call check_multiboot
+ call set_up_page_tables
+
+ ; load the new gdt
+ lgdt [GDTR.ptr]
+ jmp GDTR.gdt_cs:x86_start
+
+check_multiboot:
+ cmp eax, 0x36d76289
+ jne .no_multiboot
+ ret
+.no_multiboot:
+ mov al, "0"
+ jmp error
+
+; minimal page tables (first 8MB identity mapped)
+; core page tables will be loaded in rust
+set_up_page_tables:
+ ; map P2 table recursively
+ mov eax, p2_table
+ or eax, 0b11 ; present + writable
+ mov [p2_table + 1023 * 4], eax
+
+ ; identity map first P2 entry to a huge page
+ mov eax, 0x0 ; 0MB -> 4MB (first page)
+ or eax, 0b10000011 ; huge + present + writable
+ mov [p2_table], eax
+
+ mov eax, 0x400000 ; 4MB -> 8Mb (second page)
+ or eax, 0b10000011 ; huge + present + writable
+ mov [p2_table + 4], eax
+
+ mov eax, 0x800000 ; 8MB -> 12Mb (third page)
+ or eax, 0b10000011 ; huge + present + writable
+ mov [p2_table + 8], eax
+
+ mov eax, p2_table
+ mov cr3, eax
+ ret
+
+error:
+ mov dword [0xb8000], 0x4f524f45
+ mov dword [0xb8004], 0x4f3a4f52
+ mov dword [0xb8008], 0x4f204f20
+ mov byte [0xb800a], al
+ cli
+HALT:
+ hlt
+ jmp HALT
+
+section .bss
+align 4096
+p2_table:
+ resb 4096
+stack_bottom:
+ resb 4096 * 4
+stack_top:
+
+; minimal boot gdt (cs & ds)
+; core gdt will be loaded in rust
+section .gdt
+GDTR:
+; http://tuttlem.github.io/2014/07/11/a-gdt-primer.html
+.gdt_top:
+ DD 0, 0
+.gdt_cs: equ $ - .gdt_top; the code segment Aka KERNEL CODE
+ DW 0xffff ; Limit ( bits 0 -15 )
+ DW 0x0 ; Base ( bits 0 -15 )
+ DB 0x0 ; Base ( bits 16 -23 )
+ DB 0x9A ; [ Access Flags: 0x9A=10011010b = (present)|(Privilege Ring 0=00b)|(1)|(code => 1)|(expand down => 0)|(readable)|(0) ]
+ DB 0xCF ; [ Flags: C=1100b = (granularity)|(32bit)|(!64bit)|(0) ] / [ Limits: (bits 16-19): F=1111b ]
+ DB 0x0 ; Base ( bits 24 -31 )
+
+.gdt_ds: equ $ - .gdt_top; the data segment Aka KERNEL DATA
+ DW 0xffff ; Limit ( bits 0 -15 )
+ DW 0x0 ; Base ( bits 0 -15 )
+ DB 0x0 ; Base ( bits 16 -23 )
+ DB 0x92 ; [ Access Flags: 0x92=10010010b = (present)|(Privilege Ring 0=00b)|(1)|(data => 0)|(expand down => 0)|(readable)|(0) ]
+ DB 0xCF ; [ Flags: C=1100b = (granularity)|(32bit)|(!64bit)|(0) ] / [ Limits: (bits 16-19): F=1111b ]
+ DB 0x0 ; Base ( bits 24 -31 )
+
+.gdt_bottom:
+.ptr:
+ DW .gdt_bottom - .gdt_top - 1 ; length of the structure minus 1
+ DD .gdt_top ; pointer to top of gdt
diff --git a/kernel-rs/src/arch/x86/consts.rs b/kernel-rs/src/arch/x86/consts.rs
new file mode 100644
index 00000000..d1fb4a29
--- /dev/null
+++ b/kernel-rs/src/arch/x86/consts.rs
@@ -0,0 +1,52 @@
+// p2 layout looks like this:
+// [kernel 0->4MiB]
+// [kernel 4->8MiB]
+// [start of no man's land]
+// .
+// .
+// .
+// [end of no man's land]
+// [user stack]
+// [kernel heap]
+// [recursive map] points to first entry
+//
+// no man's land should be used in the future for mmap() I guess
+// the kernel right now takes up 2 pages with debug symbols,
+// if it gets to 3 pages everything will crash and i'll have
+// to hardcode the third page here and in `boot.asm` also!
+//
+// TODO
+// kernel is mapped identically (start of memory) but in
+// the future it should be higher-half mapped so that
+// user memory starts at 0
+
+use x86::structures::paging::*;
+use x86::*;
+use core::ops::Range;
+
+// macro_rules! prange { ($i:expr, $s:expr) => {$i..$i+$s+1}}
+
+// all of this is fucking contrived
+// the rust compiler should be able to evaluate const expressions
+// by using the impl for my structs, what i'm doing here is obvious
+// to me but not to the compiler
+
+pub const RECURSIVE_PAGE_OFFSET: VirtAddr = VirtAddr(0xffc0_000); // first 10 bits
+pub const RECURSIVE_PAGE: Page = Page::containing_address(RECURSIVE_PAGE_OFFSET);
+pub const RECURSIVE_PAGE_SIZE: u32 = 0x0040_0000; // the whole p2 entry
+
+pub const KERNEL_HEAP_OFFSET: VirtAddr = VirtAddr(RECURSIVE_PAGE_OFFSET.0 - RECURSIVE_PAGE_SIZE);
+// should be
+// pub const KERNEL_HEAP_OFFSET: VirtAddr = RECURSIVE_PAGE_OFFSET - RECURSIVE_PAGE_SIZE);
+pub const KERNEL_HEAP_SIZE: u32 = 0x0040_0000; //4MiB (1 huge page)
+pub const KERNEL_HEAP_START: Page = Page::containing_address(KERNEL_HEAP_OFFSET);
+pub const KERNEL_HEAP_END: Page =
+ Page::containing_address(VirtAddr(KERNEL_HEAP_OFFSET.0 + KERNEL_HEAP_SIZE));
+pub const KERNEL_HEAP_RANGE: Range = KERNEL_HEAP_START..KERNEL_HEAP_END;
+
+pub const USER_STACK_OFFSET: VirtAddr = VirtAddr(KERNEL_HEAP_OFFSET.0 - KERNEL_HEAP_SIZE);
+pub const USER_STACK_START: Page = Page::containing_address(USER_STACK_OFFSET);
+pub const USER_STACK_SIZE: u32 = 0x0040_0000; //4MiB (1 huge page)
+pub const USER_STACK_END: Page =
+ Page::containing_address(VirtAddr(USER_STACK_OFFSET.0 + USER_STACK_SIZE));
+pub const USER_STACK_RANGE: Range = USER_STACK_START..USER_STACK_END;
diff --git a/kernel-rs/src/arch/x86/devices/cpu.rs b/kernel-rs/src/arch/x86/devices/cpu.rs
new file mode 100644
index 00000000..111ba009
--- /dev/null
+++ b/kernel-rs/src/arch/x86/devices/cpu.rs
@@ -0,0 +1,286 @@
+extern crate raw_cpuid;
+
+use self::raw_cpuid::CpuId;
+
+pub fn cpu_info() {
+ let cpuid = CpuId::new();
+
+ if let Some(info) = cpuid.get_vendor_info() {
+ println!("Vendor: {}", info.as_string());
+ }
+
+ if let Some(info) = cpuid.get_extended_function_info() {
+ if let Some(brand) = info.processor_brand_string() {
+ println!("Model: {}", brand);
+ }
+ }
+
+ if let Some(info) = cpuid.get_processor_frequency_info() {
+ println!("Base MHz: {}", info.processor_base_frequency());
+ println!("Max MHz: {}", info.processor_max_frequency());
+ println!("Bus MHz: {}", info.bus_frequency());
+ } else {
+ set_color!(Red);
+ println!("Couldn't retrieve cpu frequency info");
+ set_color!();
+ }
+
+ if let Some(info) = cpuid.get_feature_info() {
+ print!("Features:");
+ if info.has_fpu() {
+ print!(" fpu")
+ };
+ if info.has_vme() {
+ print!(", vme")
+ };
+ if info.has_de() {
+ print!(", de")
+ };
+ if info.has_pse() {
+ print!(", pse")
+ };
+ if info.has_tsc() {
+ print!(", tsc")
+ };
+ if info.has_msr() {
+ print!(", msr")
+ };
+ if info.has_pae() {
+ print!(", pae")
+ };
+ if info.has_mce() {
+ print!(", mce")
+ };
+
+ if info.has_cmpxchg8b() {
+ print!(", cx8")
+ };
+ if info.has_apic() {
+ print!(", apic")
+ };
+ if info.has_sysenter_sysexit() {
+ print!(", sep")
+ };
+ if info.has_mtrr() {
+ print!(", mtrr")
+ };
+ if info.has_pge() {
+ print!(", pge")
+ };
+ if info.has_mca() {
+ print!(", mca")
+ };
+ if info.has_cmov() {
+ print!(", cmov")
+ };
+ if info.has_pat() {
+ print!(", pat")
+ };
+
+ if info.has_pse36() {
+ print!(", pse36")
+ };
+ if info.has_psn() {
+ print!(", psn")
+ };
+ if info.has_clflush() {
+ print!(", clflush")
+ };
+ if info.has_ds() {
+ print!(", ds")
+ };
+ if info.has_acpi() {
+ print!(", acpi")
+ };
+ if info.has_mmx() {
+ print!(", mmx")
+ };
+ if info.has_fxsave_fxstor() {
+ print!(", fxsr")
+ };
+ if info.has_sse() {
+ print!(", sse")
+ };
+
+ if info.has_sse2() {
+ print!(", sse2")
+ };
+ if info.has_ss() {
+ print!(", ss")
+ };
+ if info.has_htt() {
+ print!(", ht")
+ };
+ if info.has_tm() {
+ print!(", tm")
+ };
+ if info.has_pbe() {
+ print!(", pbe")
+ };
+
+ if info.has_sse3() {
+ print!(", sse3")
+ };
+ if info.has_pclmulqdq() {
+ print!(", pclmulqdq")
+ };
+ if info.has_ds_area() {
+ print!(", dtes64")
+ };
+ if info.has_monitor_mwait() {
+ print!(", monitor")
+ };
+ if info.has_cpl() {
+ print!(", ds_cpl")
+ };
+ if info.has_vmx() {
+ print!(", vmx")
+ };
+ if info.has_smx() {
+ print!(", smx")
+ };
+ if info.has_eist() {
+ print!(", est")
+ };
+
+ if info.has_tm2() {
+ print!(", tm2")
+ };
+ if info.has_ssse3() {
+ print!(", ssse3")
+ };
+ if info.has_cnxtid() {
+ print!(", cnxtid")
+ };
+ if info.has_fma() {
+ print!(", fma")
+ };
+ if info.has_cmpxchg16b() {
+ print!(", cx16")
+ };
+ if info.has_pdcm() {
+ print!(", pdcm")
+ };
+ if info.has_pcid() {
+ print!(", pcid")
+ };
+ if info.has_dca() {
+ print!(", dca")
+ };
+
+ if info.has_sse41() {
+ print!(", sse4_1")
+ };
+ if info.has_sse42() {
+ print!(", sse4_2")
+ };
+ if info.has_x2apic() {
+ print!(", x2apic")
+ };
+ if info.has_movbe() {
+ print!(", movbe")
+ };
+ if info.has_popcnt() {
+ print!(", popcnt")
+ };
+ if info.has_tsc_deadline() {
+ print!(", tsc_deadline_timer")
+ };
+ if info.has_aesni() {
+ print!(", aes")
+ };
+ if info.has_xsave() {
+ print!(", xsave")
+ };
+
+ if info.has_oxsave() {
+ print!(", xsaveopt")
+ };
+ if info.has_avx() {
+ print!(", avx")
+ };
+ if info.has_f16c() {
+ print!(", f16c")
+ };
+ if info.has_rdrand() {
+ print!(", rdrand")
+ };
+ println!("");
+ }
+
+ if let Some(info) = cpuid.get_extended_function_info() {
+ print!("Extended function:");
+ if info.has_64bit_mode() {
+ print!(" lm")
+ };
+ if info.has_rdtscp() {
+ print!(", rdtscp")
+ };
+ if info.has_1gib_pages() {
+ print!(", pdpe1gb")
+ };
+ if info.has_execute_disable() {
+ print!(", nx")
+ };
+ if info.has_syscall_sysret() {
+ print!(", syscall")
+ };
+ if info.has_prefetchw() {
+ print!(", prefetchw")
+ };
+ if info.has_lzcnt() {
+ print!(", lzcnt")
+ };
+ if info.has_lahf_sahf() {
+ print!(", lahf_lm")
+ };
+ if info.has_invariant_tsc() {
+ print!(", constant_tsc")
+ };
+ println!("");
+ }
+
+ if let Some(info) = cpuid.get_extended_feature_info() {
+ print!("Extended features:");
+ if info.has_fsgsbase() {
+ print!(" fsgsbase")
+ };
+ if info.has_tsc_adjust_msr() {
+ print!(", tsc_adjust")
+ };
+ if info.has_bmi1() {
+ print!(", bmi1")
+ };
+ if info.has_hle() {
+ print!(", hle")
+ };
+ if info.has_avx2() {
+ print!(", avx2")
+ };
+ if info.has_smep() {
+ print!(", smep")
+ };
+ if info.has_bmi2() {
+ print!(", bmi2")
+ };
+ if info.has_rep_movsb_stosb() {
+ print!(", erms")
+ };
+ if info.has_invpcid() {
+ print!(", invpcid")
+ };
+ if info.has_rtm() {
+ print!(", rtm")
+ };
+ // if info.has_qm() {
+ // print!(", qm")
+ // };
+ if info.has_fpu_cs_ds_deprecated() {
+ print!(", fpu_seg")
+ };
+ if info.has_mpx() {
+ print!(", mpx")
+ };
+ println!("");
+ }
+}
diff --git a/kernel-rs/src/arch/x86/devices/mod.rs b/kernel-rs/src/arch/x86/devices/mod.rs
new file mode 100644
index 00000000..212805fd
--- /dev/null
+++ b/kernel-rs/src/arch/x86/devices/mod.rs
@@ -0,0 +1,15 @@
+use x86::devices::pit;
+use x86::devices::pic;
+pub mod cpu;
+use x86::instructions::interrupts;
+
+static CHAN0_DIVISOR: u16 = 2685;
+
+pub unsafe fn init() {
+ pic::init_cascade();
+ pic::disable_irqs();
+ pic::enable_irq(0);
+ pic::enable_irq(1);
+ pit::CHAN0.set_divisor(CHAN0_DIVISOR);
+ interrupts::enable();
+}
diff --git a/kernel-rs/src/arch/x86/gdt.rs b/kernel-rs/src/arch/x86/gdt.rs
new file mode 100644
index 00000000..1ed14506
--- /dev/null
+++ b/kernel-rs/src/arch/x86/gdt.rs
@@ -0,0 +1,64 @@
+use x86::structures::gdt;
+use x86::structures::tss;
+use x86::structures::gdt::SegmentSelector;
+use x86::instructions::segmentation::*;
+use x86::instructions::tables::load_tss;
+use x86::PrivilegeLevel::{Ring0, Ring3};
+
+pub static mut GDT: gdt::Gdt = gdt::Gdt::new();
+pub static mut TSS: tss::TaskStateSegment = tss::TaskStateSegment::new();
+pub static mut TASK_TSS: tss::TaskStateSegment = tss::TaskStateSegment::new();
+
+pub static GDT_KERNEL_CODE: SegmentSelector = SegmentSelector::new(1, Ring0);
+pub static GDT_KERNEL_DATA: SegmentSelector = SegmentSelector::new(2, Ring0);
+pub static GDT_USER_CODE: SegmentSelector = SegmentSelector::new(3, Ring3);
+pub static GDT_USER_DATA: SegmentSelector = SegmentSelector::new(4, Ring3);
+pub static GDT_TSS: SegmentSelector = SegmentSelector::new(5, Ring3);
+pub static GDT_TASK_TSS: SegmentSelector = SegmentSelector::new(7, Ring3);
+
+pub unsafe fn init() {
+ // the following *order* is important
+ let kcode_selector = GDT.add_entry(gdt::Descriptor::kernel_code_segment());
+ let kdata_selector = GDT.add_entry(gdt::Descriptor::kernel_data_segment());
+ let ucode_selector = GDT.add_entry(gdt::Descriptor::user_code_segment());
+ let udata_selector = GDT.add_entry(gdt::Descriptor::user_data_segment());
+
+ //I read that the tss should be twice as long
+ //fuck knows why...
+ TSS.ss0 = GDT_KERNEL_CODE.0;
+ asm!("mov %esp, $0" : "=r" (TSS.esp0));
+ let tss_selector = GDT.add_entry(gdt::Descriptor::tss_segment(&TSS));
+ GDT.add_entry(gdt::Descriptor(0));
+
+ TASK_TSS.eip = self::test_task as *const () as u32;
+ let task_tss_selector = GDT.add_entry(gdt::Descriptor::tss_segment(&TASK_TSS));
+ GDT.add_entry(gdt::Descriptor(0));
+
+ assert_eq!(kcode_selector, GDT_KERNEL_CODE);
+ assert_eq!(kdata_selector, GDT_KERNEL_DATA);
+ assert_eq!(ucode_selector, GDT_USER_CODE);
+ assert_eq!(udata_selector, GDT_USER_DATA);
+ assert_eq!(tss_selector, GDT_TSS);
+ assert_eq!(task_tss_selector, GDT_TASK_TSS);
+
+ // use x86::structures::gdt::Descriptor;
+ // println!(
+ // "tr({:#x}):\n {:#?}",
+ // tss_selector.0,
+ // gdt::Descriptor(GDT.table[tss_selector.index() as usize])
+ // );
+ // flush!();
+
+ GDT.load();
+ set_cs(kcode_selector);
+ load_ds(kdata_selector);
+ load_es(kdata_selector);
+ load_ss(kdata_selector);
+ load_tss(tss_selector);
+ // unreachable!();
+}
+
+pub fn test_task() {
+ println!("inside test task omg we did it !!!");
+ flush!();
+}
diff --git a/kernel-rs/src/arch/x86/grub.cfg b/kernel-rs/src/arch/x86/grub.cfg
new file mode 100644
index 00000000..7bccc42f
--- /dev/null
+++ b/kernel-rs/src/arch/x86/grub.cfg
@@ -0,0 +1,7 @@
+set timeout=0
+set default=0
+
+menuentry "Blue Snow" {
+ multiboot2 /boot/bluesnow
+ boot
+}
diff --git a/kernel-rs/src/arch/x86/idt.rs b/kernel-rs/src/arch/x86/idt.rs
new file mode 100644
index 00000000..ec5ca5e4
--- /dev/null
+++ b/kernel-rs/src/arch/x86/idt.rs
@@ -0,0 +1,52 @@
+use x86::structures::idt::*;
+use super::interrupt::*;
+
+lazy_static! {
+ static ref IDT: Idt = {
+ let mut idt = Idt::new();
+
+ // set up CPU exceptions
+ idt.breakpoint.set_handler_fn(exception::breakpoint);
+ idt.debug.set_handler_fn(exception::debug);
+ idt.non_maskable_interrupt.set_handler_fn(exception::non_maskable);
+ idt.breakpoint.set_handler_fn(exception::breakpoint);
+ idt.overflow.set_handler_fn(exception::overflow);
+ idt.bound_range_exceeded.set_handler_fn(exception::bound_range);
+ idt.invalid_opcode.set_handler_fn(exception::invalid_opcode);
+ idt.device_not_available.set_handler_fn(exception::device_not_available);
+ idt.double_fault.set_handler_fn(exception::double_fault);
+ idt.segment_not_present.set_handler_fn(exception::segment_not_present);
+ idt.stack_segment_fault.set_handler_fn(exception::stack_segment);
+ idt.general_protection_fault.set_handler_fn(exception::general_protection);
+ idt.page_fault.set_handler_fn(exception::page_fault);
+ idt.x87_floating_point.set_handler_fn(exception::x87_fpu);
+ idt.alignment_check.set_handler_fn(exception::alignment_check);
+ idt.machine_check.set_handler_fn(exception::machine_check);
+ idt.simd_floating_point.set_handler_fn(exception::simd);
+ idt.virtualization.set_handler_fn(exception::virtualization);
+
+ // set up IRQs
+ idt[32].set_handler_fn(irq::pit);
+ idt[33].set_handler_fn(irq::keyboard);
+ idt[34].set_handler_fn(irq::cascade);
+ idt[35].set_handler_fn(irq::com2);
+ idt[36].set_handler_fn(irq::com1);
+ idt[37].set_handler_fn(irq::lpt2);
+ idt[38].set_handler_fn(irq::floppy);
+ idt[39].set_handler_fn(irq::lpt1);
+ idt[40].set_handler_fn(irq::rtc);
+ idt[41].set_handler_fn(irq::pci1);
+ idt[42].set_handler_fn(irq::pci2);
+ idt[43].set_handler_fn(irq::pci3);
+ idt[44].set_handler_fn(irq::mouse);
+ idt[45].set_handler_fn(irq::fpu);
+ idt[46].set_handler_fn(irq::ata1);
+ idt[47].set_handler_fn(irq::ata2);
+
+ idt
+ };
+}
+
+pub fn init() {
+ IDT.load();
+}
diff --git a/kernel-rs/src/arch/x86/interrupt/exception.rs b/kernel-rs/src/arch/x86/interrupt/exception.rs
new file mode 100644
index 00000000..4f86fca8
--- /dev/null
+++ b/kernel-rs/src/arch/x86/interrupt/exception.rs
@@ -0,0 +1,80 @@
+// https://wiki.osdev.org/Exceptions
+
+macro_rules! exception {
+ ($name:ident, $func:block) => {
+ pub extern "x86-interrupt" fn $name(stack_frame: &mut ExceptionStackFrame)
+ {
+ println!("#{}", stringify!($name));
+ println!("{:#?}", stack_frame);
+ flush!();
+
+ #[allow(unused_variables)]
+ fn inner(stack: &mut ExceptionStackFrame) {
+ $func
+ }
+ inner(stack_frame);
+ }
+ }
+}
+
+macro_rules! exception_err {
+ ($name:ident, $func:block) => {
+ pub extern "x86-interrupt" fn $name(
+ stack_frame: &mut ExceptionStackFrame, error_code: u32)
+ {
+ println!("#{}({})", stringify!($name), error_code);
+ println!("{:#?}", stack_frame);
+ flush!();
+
+ #[allow(unused_variables)]
+ fn inner(stack: &mut ExceptionStackFrame) {
+ $func
+ }
+ inner(stack_frame);
+ }
+ }
+}
+
+use x86::structures::idt::*;
+
+exception!(divide_by_zero, {
+ panic!("CPU exception: division by zero")
+});
+
+exception!(debug, {});
+exception!(non_maskable, {});
+exception!(breakpoint, {});
+exception!(overflow, {});
+exception!(bound_range, {});
+exception!(invalid_opcode, {});
+exception!(device_not_available, {});
+exception_err!(double_fault, {
+ panic!("double fault non recoverable");
+});
+exception!(coprocessor_segment_overrun, {});
+exception_err!(invalid_tss, {});
+exception_err!(segment_not_present, {});
+exception_err!(stack_segment, {});
+exception_err!(general_protection, {
+ panic!("cannot recover from #GP");
+});
+
+pub extern "x86-interrupt" fn page_fault(
+ stack_frame: &mut ExceptionStackFrame,
+ code: PageFaultErrorCode,
+) {
+ use x86::registers::control::Cr2;
+ println!("Exception: page_fault");
+ println!("Error code: {:?}", code);
+ println!("PFLA: {:?}", Cr2::read());
+ println!("{:#?}", stack_frame);
+ flush!();
+ panic!("cannot recover from #PF")
+}
+
+exception!(x87_fpu, {});
+exception_err!(alignment_check, {});
+exception!(machine_check, {});
+exception!(simd, {});
+exception!(virtualization, {});
+exception_err!(security, {});
diff --git a/kernel-rs/src/arch/x86/interrupt/irq.rs b/kernel-rs/src/arch/x86/interrupt/irq.rs
new file mode 100644
index 00000000..0a6e349c
--- /dev/null
+++ b/kernel-rs/src/arch/x86/interrupt/irq.rs
@@ -0,0 +1,80 @@
+use x86::structures::idt::*;
+use x86::devices::pic;
+use time;
+
+#[macro_export]
+macro_rules! interrupt {
+ ($i:expr, $name:ident, $func:block) => {
+ pub extern "x86-interrupt" fn $name(stack_frame: &mut ExceptionStackFrame)
+ {
+ unsafe { trigger($i); }
+
+ #[allow(unused_variables)]
+ fn inner(stack: &mut ExceptionStackFrame) {
+ $func
+ }
+ inner(stack_frame);
+
+ unsafe { acknowledge($i); }
+ }
+ }
+}
+
+pub unsafe fn trigger(irq: u8) {
+ if irq < 16 {
+ if irq >= 8 {
+ pic::SLAVE.mask_set(irq - 8);
+ pic::MASTER.ack();
+ pic::SLAVE.ack();
+ } else {
+ pic::MASTER.mask_set(irq);
+ pic::MASTER.ack();
+ }
+ }
+}
+
+pub unsafe fn acknowledge(irq: usize) {
+ if irq < 16 {
+ if irq >= 8 {
+ pic::SLAVE.mask_clear(irq as u8 - 8);
+ pic::SLAVE.ack();
+ } else {
+ pic::MASTER.mask_clear(irq as u8);
+ pic::MASTER.ack();
+ }
+ }
+}
+
+interrupt!(0, pit, {
+ /// t = 1/f
+ /// pit freq = 1.193182 MHz
+ /// chan0 divisor = 2685
+ /// PIT_RATE in us
+ const PIT_RATE: u32 = 2_251;
+ {
+ let mut offset = time::OFFSET.lock();
+ let sum = offset.1 + PIT_RATE;
+ offset.1 = sum % 1_000_000;
+ offset.0 += sum / 1_000_000;
+ }
+ unsafe { pic::MASTER.ack() };
+});
+
+interrupt!(1, keyboard, {
+ ::keyboard::kbd_callback();
+});
+
+interrupt!(2, cascade, {});
+interrupt!(3, com2, {});
+interrupt!(4, com1, {});
+interrupt!(5, lpt2, {});
+interrupt!(6, floppy, {});
+interrupt!(7, lpt1, {});
+interrupt!(8, rtc, {});
+interrupt!(9, pci1, {});
+interrupt!(10, pci2, {});
+interrupt!(11, pci3, {});
+interrupt!(12, mouse, {});
+interrupt!(13, fpu, {});
+interrupt!(14, ata1, {});
+interrupt!(15, ata2, {});
diff --git a/kernel-rs/src/arch/x86/interrupt/mod.rs b/kernel-rs/src/arch/x86/interrupt/mod.rs
new file mode 100644
index 00000000..c8a97f14
--- /dev/null
+++ b/kernel-rs/src/arch/x86/interrupt/mod.rs
@@ -0,0 +1,4 @@
+#[macro_use]
+pub mod exception;
+#[macro_use]
+pub mod irq;
diff --git a/kernel-rs/src/arch/x86/linker.ld b/kernel-rs/src/arch/x86/linker.ld
new file mode 100644
index 00000000..96419d19
--- /dev/null
+++ b/kernel-rs/src/arch/x86/linker.ld
@@ -0,0 +1,80 @@
+ENTRY(start)
+OUTPUT_FORMAT(elf32-i386)
+
+SECTIONS {
+ /* VGA, cannot use section for this */
+ VGA_PTR = 0xb8000;
+ . = 0xb8000;
+ . += 80 * 25 * 2;
+
+ . = 1M;
+ /* ensure that the multiboot header is at the beginning */
+ .multiboot :
+ {
+ /* KEEP otherwise it gets garbage collected by linker */
+ KEEP(*(.multiboot))
+ . = ALIGN(4K);
+ }
+
+ .text :
+ {
+ *(.text .text.*)
+ . = ALIGN(4K);
+ }
+
+ .rodata :
+ {
+ *(.rodata .rodata.*)
+ . = ALIGN(4K);
+ }
+
+ .data :
+ {
+ *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) *(.data.*)
+ . = ALIGN(4K);
+ }
+
+ /* NOT A GOOD IDEA TO GROUP debug_* SYMBOLS ! */
+ /* .debug : */
+ /* { */
+ /* /1* KEEP(*(.debug_*)) *1/ */
+ /* *(.debug_*) */
+ /* . = ALIGN(4K); */
+ /* } */
+
+ .gdt :
+ {
+ *(.gdt)
+ . = ALIGN(4K);
+ }
+
+ .got :
+ {
+ *(.got)
+ . = ALIGN(4K);
+ }
+
+ .got.plt :
+ {
+ *(.got.plt)
+ . = ALIGN(4K);
+ }
+
+ .bss :
+ {
+ *(.bss .bss.*)
+ . = ALIGN(4K);
+ }
+
+ /* .stab : */
+ /* { */
+ /* KEEP(*(.stab)) */
+ /* . = ALIGN(4K); */
+ /* } */
+
+ /* .stabstr : */
+ /* { */
+ /* KEEP(*(.stabstr)) */
+ /* . = ALIGN(4K); */
+ /* } */
+}
diff --git a/kernel-rs/src/arch/x86/mod.rs b/kernel-rs/src/arch/x86/mod.rs
new file mode 100644
index 00000000..14cd6855
--- /dev/null
+++ b/kernel-rs/src/arch/x86/mod.rs
@@ -0,0 +1,141 @@
+extern crate x86;
+
+#[macro_use]
+pub mod paging;
+pub mod interrupt;
+pub mod devices;
+pub mod consts;
+
+pub mod gdt;
+pub mod idt;
+
+use multiboot2;
+use acpi;
+
+#[no_mangle]
+pub unsafe extern "C" fn x86_rust_start(multiboot_info_addr: usize) {
+ // parse multiboot2 info
+ let boot_info = multiboot2::load(multiboot_info_addr);
+
+ // ACPI must be intialized BEFORE paging is active
+ if let Some(rsdp) = boot_info.rsdp_v2_tag() {
+ acpi::load(rsdp).expect("ACPI failed");
+ } else if let Some(rsdp) = boot_info.rsdp_tag() {
+ acpi::load(rsdp).expect("ACPI failed");
+ } else {
+ acpi::init().expect("ACPI failed");
+ }
+
+ // set up physical allocator
+ ::memory::init(&boot_info);
+
+ // set up virtual addressing (paging)
+ let mut active_table = paging::init(&boot_info);
+
+ // load idt (exceptions + irqs)
+ idt::init();
+
+ // fill and load gdt
+ gdt::init();
+
+ // set up heap
+ ::allocator::init(&mut active_table);
+
+ // set up user stack
+ // use x86::structures::paging::*;
+ // for page in ::USER_STACK_RANGE {
+ // active_table.map(page, PageTableFlags::WRITABLE);
+ // }
+
+ // set up pic, pit
+ devices::init();
+
+ // primary CPU entry point, architutecture independant now.
+ ::kmain();
+}
+
+pub unsafe fn switch(ip: u32) -> ! {
+ asm!("push $0; ret" :: "r"(ip) :: "volatile", "intel");
+ unreachable!();
+}
+
+pub unsafe fn usermode(ip: u32, sp: u32) -> ! {
+ // use x86::structures::gdt::{Descriptor, SegmentSelector};
+ // use x86::instructions::segmentation::*;
+ // use x86::PrivilegeLevel::{Ring0, Ring3};
+
+ x86::instructions::interrupts::disable();
+
+ // println!("sp: {:#x}", sp);
+ // println!("ip: {:#x}", ip);
+
+ // load_ds(udata_selector);
+ // asm!("mov $0, %ds" :: "r"(gdt::GDT_USER_DATA << 3 | 3));
+
+ // loop {}
+
+ // load_es(udata_selector);
+ // load_fs(udata_selector);
+ // load_gs(udata_selector);
+
+ use x86::registers::flags;
+ flags::set_flags(flags::Flags::NT);
+
+ // asm!("mov %esp, $0" : "=r" (sp));
+
+ println!("{:#x}", gdt::GDT_KERNEL_DATA.0);
+ println!("{:#x}", sp);
+ println!("{:#x}", 1 << 9);
+ println!("{:#x}", gdt::GDT_KERNEL_CODE.0);
+ println!("{:#x}", ip);
+ flush!();
+
+ asm!("
+ push $0; \
+ push $1; \
+ push $2; \
+ push $3; \
+ push $4"
+ : //no output
+ : "r"(gdt::GDT_USER_DATA.0),
+ "r"(sp),
+ "r"(1 << 9) // interrupt enable flag
+ "r"(gdt::GDT_USER_CODE.0),
+ "r"(ip)
+ : //no clobbers
+ : "intel", "volatile"
+ );
+
+ // loop {}
+
+ asm!("iret");
+
+ unreachable!();
+}
+
+/// Dump control registers
+pub fn regs() {
+ use x86::registers::control::*;
+ use x86::instructions::tables::tr;
+ use x86::instructions::segmentation::*;
+ use x86::registers::flags::*;
+ use x86::structures::gdt;
+ println!("cr0 = {:?}", Cr0::read());
+ println!("cr3 = {:?}", Cr3::read());
+ println!("cr4 = {:?}", Cr4::read());
+ println!("flags= {:?}", flags());
+ println!("tr = {:?}", tr());
+ println!("ss = {:?}", ss());
+ println!("cs = {:?}", cs());
+ println!("ds = {:?}", ds());
+ println!("es = {:?}", es());
+ println!("fs = {:?}", fs());
+ println!("gs = {:?}", gs());
+ unsafe {
+ println!(
+ "tss = {:#?}",
+ gdt::Descriptor(::arch::x86::gdt::GDT.table[tr().index() as usize])
+ );
+ }
+ flush!();
+}
diff --git a/kernel-rs/src/arch/x86/multiboot_header.asm b/kernel-rs/src/arch/x86/multiboot_header.asm
new file mode 100644
index 00000000..c843f927
--- /dev/null
+++ b/kernel-rs/src/arch/x86/multiboot_header.asm
@@ -0,0 +1,16 @@
+section .multiboot
+header_start:
+align 4
+ dd 0xe85250d6 ; magic number (multiboot 2)
+ dd 0 ; TODO change it because architecture 0 means(protected mode i386) We could have problem here
+ dd header_end - header_start ; header length
+
+ dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)); checksum
+
+ ; insert optional multiboot tags here
+
+ ; required end tag
+ dw 0 ; type
+ dw 0 ; flags
+ dd 8 ; size
+header_end:
diff --git a/kernel-rs/src/arch/x86/paging/mapper.rs b/kernel-rs/src/arch/x86/paging/mapper.rs
new file mode 100644
index 00000000..cf8ce6b5
--- /dev/null
+++ b/kernel-rs/src/arch/x86/paging/mapper.rs
@@ -0,0 +1,89 @@
+use core::ptr::Unique;
+use x86::structures::paging::*;
+use x86::instructions::tlb;
+use x86::usize_conversions::usize_from;
+use x86::*;
+use super::table::RecTable;
+
+// virtual address of recursively mapped P2
+// for protected mode non PAE
+// https://wiki.osdev.org/Page_Tables
+pub const P2: *mut PageTable = 0xffff_f000 as *mut _;
+
+pub struct Mapper {
+ pub p2: Unique,
+}
+
+impl Mapper {
+ pub unsafe fn new() -> Mapper {
+ Mapper {
+ p2: Unique::new_unchecked(self::P2),
+ }
+ }
+
+ // the remaining mapping methods, all public
+ pub fn p2(&self) -> &PageTable {
+ unsafe { self.p2.as_ref() }
+ }
+
+ pub fn p2_mut(&mut self) -> &mut PageTable {
+ unsafe { self.p2.as_mut() }
+ }
+
+ /// virtual addr to physical addr translation
+ pub fn translate(&self, virtual_address: VirtAddr) -> Option {
+ let offset = virtual_address.as_u32() % PAGE_SIZE as u32;
+ self.translate_page(Page::containing_address(virtual_address))
+ .map(|frame| frame.start_address() + offset)
+ }
+
+ /// virtual page to physical frame translation
+ pub fn translate_page(&self, page: Page) -> Option {
+ let p1 = self.p2().next_table(usize_from(u32::from(page.p2_index())));
+
+ let huge_page = || {
+ let p2_entry = &self.p2()[page.p2_index()];
+ if let Some(start_frame) = p2_entry.pointed_frame() {
+ if p2_entry.flags().contains(PageTableFlags::HUGE_PAGE) {
+ // TODO 4MiB alignment check
+ return Some(start_frame + u32::from(page.p1_index()));
+ }
+ }
+ None
+ };
+
+ p1.and_then(|p1| p1[page.p1_index()].pointed_frame())
+ .or_else(huge_page)
+ }
+
+ /// map a virtual page to a physical frame in the page tables
+ pub fn map_to(&mut self, page: Page, frame: PhysFrame, flags: PageTableFlags) {
+ let p2 = self.p2_mut();
+ let p1 = p2.next_table_create(usize_from(u32::from(page.p2_index())));
+ assert!(p1[page.p1_index()].is_unused());
+ p1[page.p1_index()].set(frame, flags | PageTableFlags::PRESENT);
+ }
+
+ pub fn map(&mut self, page: Page, flags: PageTableFlags) {
+ let frame = ::memory::allocate_frames(1).expect("out of frames");
+ self.map_to(page, frame, flags)
+ }
+
+ pub fn identity_map(&mut self, frame: PhysFrame, flags: PageTableFlags) {
+ let virt_addr = VirtAddr::new(frame.start_address().as_u32());
+ let page = Page::containing_address(virt_addr);
+ self.map_to(page, frame, flags);
+ }
+
+ pub fn unmap(&mut self, page: Page) {
+ assert!(self.translate(page.start_address()).is_some());
+
+ let p1 = self.p2_mut()
+ .next_table_mut(usize_from(u32::from(page.p2_index())))
+ .expect("mapping code does not support huge pages");
+ let frame = p1[page.p1_index()].pointed_frame().unwrap();
+ p1[page.p1_index()].set_unused();
+ tlb::flush(page.start_address());
+ ::memory::deallocate_frames(frame, 1);
+ }
+}
diff --git a/kernel-rs/src/arch/x86/paging/mod.rs b/kernel-rs/src/arch/x86/paging/mod.rs
new file mode 100644
index 00000000..698d2cf9
--- /dev/null
+++ b/kernel-rs/src/arch/x86/paging/mod.rs
@@ -0,0 +1,189 @@
+#![allow(dead_code)]
+
+mod table;
+mod temporary_page;
+mod mapper;
+
+// use memory::*;
+use self::mapper::Mapper;
+use self::temporary_page::TemporaryPage;
+use core::ops::{Deref, DerefMut};
+use multiboot2::BootInformation;
+use x86::*;
+use x86::registers::control::Cr3;
+use x86::instructions::tlb;
+use x86::structures::paging::*;
+use multiboot2;
+
+/// should be called only once
+pub fn init(boot_info: &multiboot2::BootInformation) -> ActivePageTable {
+ use x86::registers::control::*;
+ Cr4::add(Cr4Flags::PSE);
+ Cr0::add(Cr0Flags::PAGING | Cr0Flags::WRITE_PROTECT);
+
+ let active_table = remap_the_kernel(boot_info);
+
+ active_table
+}
+
+pub struct ActivePageTable {
+ mapper: Mapper,
+}
+
+impl Deref for ActivePageTable {
+ type Target = Mapper;
+
+ fn deref(&self) -> &Mapper {
+ &self.mapper
+ }
+}
+
+impl DerefMut for ActivePageTable {
+ fn deref_mut(&mut self) -> &mut Mapper {
+ &mut self.mapper
+ }
+}
+
+impl ActivePageTable {
+ pub unsafe fn new() -> ActivePageTable {
+ ActivePageTable {
+ mapper: Mapper::new(),
+ }
+ }
+
+ pub fn with(
+ &mut self,
+ table: &mut InactivePageTable,
+ temporary_page: &mut temporary_page::TemporaryPage,
+ f: F,
+ ) where
+ F: FnOnce(&mut Mapper),
+ {
+ let (cr3_back, _cr3flags_back) = Cr3::read();
+
+ // map temp page to current p2
+ let p2_table = temporary_page.map_table_frame(cr3_back.clone(), self);
+
+ // overwrite recursive map
+ self.p2_mut()[1023].set(
+ table.p2_frame.clone(),
+ PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
+ );
+ tlb::flush_all();
+
+ // execute f in the new context
+ f(self);
+
+ // restore recursive mapping to original p2 table
+ p2_table[1023].set(cr3_back, PageTableFlags::PRESENT | PageTableFlags::WRITABLE);
+ }
+
+ pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
+ let (p2_frame, cr3_flags) = Cr3::read();
+ let old_table = InactivePageTable { p2_frame };
+
+ unsafe {
+ Cr3::write(new_table.p2_frame, cr3_flags);
+ }
+
+ old_table
+ }
+}
+
+pub struct InactivePageTable {
+ p2_frame: PhysFrame,
+}
+
+impl InactivePageTable {
+ pub fn new(
+ frame: PhysFrame,
+ active_table: &mut ActivePageTable,
+ temporary_page: &mut TemporaryPage,
+ ) -> InactivePageTable {
+ {
+ let table = temporary_page.map_table_frame(frame.clone(), active_table);
+
+ table.zero();
+ // set up recursive mapping for the table
+ table[1023].set(
+ frame.clone(),
+ PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
+ )
+ }
+ temporary_page.unmap(active_table);
+ InactivePageTable { p2_frame: frame }
+ }
+}
+
+pub fn remap_the_kernel(boot_info: &BootInformation) -> ActivePageTable {
+ let mut temporary_page = TemporaryPage::new(Page { number: 0xcafe });
+ let mut active_table = unsafe { ActivePageTable::new() };
+ let mut new_table = {
+ let frame = ::memory::allocate_frames(1).expect("no more frames");
+ InactivePageTable::new(frame, &mut active_table, &mut temporary_page)
+ };
+
+ active_table.with(&mut new_table, &mut temporary_page, |mapper| {
+ // id map vga buffer
+ let vga_buffer_frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
+ mapper.identity_map(vga_buffer_frame, PageTableFlags::WRITABLE);
+
+ let elf_sections_tag = boot_info
+ .elf_sections_tag()
+ .expect("Memory map tag required");
+
+ // id map kernel sections
+ for section in elf_sections_tag.sections() {
+ if !section.is_allocated() {
+ continue;
+ }
+ assert!(
+ section.start_address() % PAGE_SIZE as u64 == 0,
+ "sections need to be page aligned"
+ );
+
+ let flags = elf_to_pagetable_flags(§ion.flags());
+ let start_frame =
+ PhysFrame::containing_address(PhysAddr::new(section.start_address() as u32));
+ let end_frame =
+ PhysFrame::containing_address(PhysAddr::new(section.end_address() as u32 - 1));
+ for frame in start_frame..end_frame + 1 {
+ mapper.identity_map(frame, flags);
+ }
+ }
+
+ // id map multiboot
+ let multiboot_start =
+ PhysFrame::containing_address(PhysAddr::new(boot_info.start_address() as u32));
+ let multiboot_end =
+ PhysFrame::containing_address(PhysAddr::new(boot_info.end_address() as u32 - 1));
+ for frame in multiboot_start..multiboot_end + 1 {
+ mapper.identity_map(frame, PageTableFlags::PRESENT);
+ }
+ });
+
+ let old_table = active_table.switch(new_table);
+
+ let old_p2_page =
+ Page::containing_address(VirtAddr::new(old_table.p2_frame.start_address().as_u32()));
+
+ active_table.unmap(old_p2_page);
+
+ active_table
+}
+
+fn elf_to_pagetable_flags(elf_flags: &multiboot2::ElfSectionFlags) -> PageTableFlags {
+ use multiboot2::ElfSectionFlags;
+
+ let mut flags = PageTableFlags::empty();
+
+ if elf_flags.contains(ElfSectionFlags::ALLOCATED) {
+ // section is loaded to memory
+ flags = flags | PageTableFlags::PRESENT;
+ }
+ if elf_flags.contains(ElfSectionFlags::WRITABLE) {
+ flags = flags | PageTableFlags::WRITABLE;
+ }
+
+ flags
+}
diff --git a/kernel-rs/src/arch/x86/paging/table.rs b/kernel-rs/src/arch/x86/paging/table.rs
new file mode 100644
index 00000000..3b774172
--- /dev/null
+++ b/kernel-rs/src/arch/x86/paging/table.rs
@@ -0,0 +1,47 @@
+use x86::structures::paging::*;
+
+pub trait RecTable {
+ fn next_table_address(&self, index: usize) -> Option;
+ fn next_table(&self, index: usize) -> Option<&PageTable>;
+ fn next_table_mut(&mut self, index: usize) -> Option<&mut PageTable>;
+ fn next_table_create(&mut self, index: usize) -> &mut PageTable;
+}
+
+impl RecTable for PageTable {
+ fn next_table_address(&self, index: usize) -> Option {
+ let entry_flags = self[index].flags();
+ if entry_flags.contains(PageTableFlags::PRESENT)
+ && !entry_flags.contains(PageTableFlags::HUGE_PAGE)
+ {
+ let table_address = self as *const _ as usize;
+ Some((table_address << 10 | index << 12) as u32)
+ } else {
+ None
+ }
+ }
+
+ fn next_table(&self, index: usize) -> Option<&PageTable> {
+ self.next_table_address(index)
+ .map(|address| unsafe { &*(address as *const _) })
+ }
+
+ fn next_table_mut(&mut self, index: usize) -> Option<&mut PageTable> {
+ self.next_table_address(index)
+ .map(|address| unsafe { &mut *(address as *mut _) })
+ }
+
+ fn next_table_create(&mut self, index: usize) -> &mut PageTable {
+ if self.next_table(index).is_none() {
+ assert!(
+ !self[index].flags().contains(PageTableFlags::HUGE_PAGE),
+ "mapping code does not support huge pages"
+ );
+ let frame = ::memory::allocate_frames(1).expect("out of memory");
+ self[index].set(frame, PageTableFlags::PRESENT | PageTableFlags::WRITABLE);
+ self.next_table_mut(index)
+ .expect("next_table_mut gave None")
+ .zero()
+ }
+ self.next_table_mut(index).expect("no next table 2")
+ }
+}
diff --git a/kernel-rs/src/arch/x86/paging/temporary_page.rs b/kernel-rs/src/arch/x86/paging/temporary_page.rs
new file mode 100644
index 00000000..b712fcfb
--- /dev/null
+++ b/kernel-rs/src/arch/x86/paging/temporary_page.rs
@@ -0,0 +1,44 @@
+use super::ActivePageTable;
+use x86::*;
+use x86::structures::paging::*;
+
+pub struct TemporaryPage {
+ pub page: Page,
+}
+
+impl TemporaryPage {
+ pub fn new(page: Page) -> TemporaryPage {
+ TemporaryPage { page: page }
+ }
+
+ /// Maps the temporary page to the given frame in the active table.
+ /// Returns the start address of the temporary page.
+ pub fn map(&mut self, frame: PhysFrame, active_table: &mut ActivePageTable) -> VirtAddr {
+ assert!(
+ active_table.translate_page(self.page).is_none(),
+ "temporary page is already mapped"
+ );
+ active_table.map_to(self.page, frame, PageTableFlags::WRITABLE);
+ // this kind of check should be done in a test routine
+ assert!(
+ active_table.translate_page(self.page).is_some(),
+ "temporary page was not mapped"
+ );
+ self.page.start_address()
+ }
+
+ /// Unmaps the temporary page in the active table.
+ pub fn unmap(&mut self, active_table: &mut ActivePageTable) {
+ active_table.unmap(self.page)
+ }
+
+ /// Maps the temporary page to the given page table frame in the active
+ /// table. Returns a reference to the now mapped table.
+ pub fn map_table_frame(
+ &mut self,
+ frame: PhysFrame,
+ active_table: &mut ActivePageTable,
+ ) -> &mut PageTable {
+ unsafe { &mut *(self.map(frame, active_table).as_u32() as *mut PageTable) }
+ }
+}
diff --git a/kernel-rs/src/arch/x86/start.asm b/kernel-rs/src/arch/x86/start.asm
new file mode 100644
index 00000000..fc0fc608
--- /dev/null
+++ b/kernel-rs/src/arch/x86/start.asm
@@ -0,0 +1,18 @@
+global x86_start
+extern x86_rust_start
+
+section .text
+bits 32
+x86_start:
+ mov ax, 0x10 ; 16 bytes (0x10) is where the offset for data section (gdt_ds)
+ mov ds, ax
+ mov ss, ax
+ mov es, ax
+ mov ax, 0x0 ; fuck fs & gs, fuck them.
+ mov fs, ax
+ mov gs, ax
+
+ call x86_rust_start
+HALT:
+ hlt
+ jmp HALT
diff --git a/kernel-rs/src/console.rs b/kernel-rs/src/console.rs
new file mode 100644
index 00000000..fa6b5fa5
--- /dev/null
+++ b/kernel-rs/src/console.rs
@@ -0,0 +1,126 @@
+use vga::*;
+use alloc::collections::BTreeMap;
+
+pub static mut CONSOLE: Console = self::Console::new();
+
+lazy_static! {
+ static ref COMMANDS: BTreeMap<&'static str, fn()> = {
+ let mut commands = BTreeMap::new();
+ commands.insert("help", self::help as fn());
+
+ // ACPI
+ commands.insert("acpi", ::acpi::info as fn());
+ commands.insert("reboot", ::acpi::reboot as fn());
+ commands.insert("shutdown", ::acpi::shutdown as fn());
+
+ // time
+ commands.insert("uptime", ::time::uptime as fn());
+
+ // cpu
+ use arch::x86;
+ commands.insert("cpu", x86::devices::cpu::cpu_info as fn());
+ commands.insert("regs", x86::regs as fn());
+ commands.insert("int3", ::x86::instructions::interrupts::int3 as fn());
+
+ //memory
+ commands.insert("stack", ::memory::print_stack as fn());
+ commands.insert("page_fault", ::memory::page_fault as fn());
+ commands.insert("overflow", ::memory::overflow as fn());
+
+ //pci
+ commands.insert("lspci", ::pci::lspci as fn());
+ commands
+ };
+}
+
+fn help() {
+ println!("The following commands are available:");
+ for (key, _val) in COMMANDS.iter() {
+ println!("{}", key);
+ }
+}
+
+pub struct Console {
+ command: [u8; 10],
+ command_len: usize,
+}
+
+impl Console {
+ pub const fn new() -> Console {
+ Console {
+ command: [b'\0'; 10],
+ command_len: 0,
+ }
+ }
+
+ pub fn init(&self) {
+ set_color!();
+ // print!("{}", format_args!("{: ^4000}", r#" "#));
+ unsafe {
+ // VGA.buffer_pos = 0;
+ self.prompt();
+ VGA.flush();
+ }
+ }
+
+ pub fn backspace(&mut self) {
+ if self.command_len > 0 {
+ self.command_len -= 1;
+ unsafe { VGA.erase_byte(); }
+ }
+ }
+
+ pub fn prompt(&self) {
+ set_color!(Blue);
+ unsafe { VGA.write_str("> "); }
+ set_color!();
+ flush!();
+ }
+
+ pub fn keypress(&mut self, ascii: u8) {
+ match ascii {
+ b'\n' if self.command_len == 0 => {
+ unsafe { VGA.write_byte(b'\n'); }
+ self.prompt();
+ }
+ b'\n' => {
+ unsafe { VGA.write_byte(b'\n'); }
+ self.exec();
+ self.command_len = 0;
+ self.prompt();
+ }
+ // _ if self.command_len >= 10 => (),
+ // byte if self.command_len == 0 && byte == b' ' => (),
+ byte => {
+ if self.command_len >= 10 {
+ return;
+ };
+ self.command[self.command_len] = byte;
+ unsafe { VGA.write_byte(byte); }
+ self.command_len += 1;
+ }
+ }
+ unsafe { VGA.flush(); }
+ }
+
+ fn get_command(&self) -> Result {
+ match core::str::from_utf8(&self.command) {
+ Ok(y) => {
+ if let Some(command) = COMMANDS.get(&y[..self.command_len]) {
+ Ok(*command)
+ } else {
+ Err("Command not found, try help")
+ }
+ },
+ Err(_) => Err("Command is not utf8"),
+ }
+ }
+
+ pub fn exec(&self) {
+ let command = self.get_command();
+ match command {
+ Err(msg) => println!("{}", msg),
+ Ok(func) => (func)(),
+ }
+ }
+}
diff --git a/kernel-rs/src/keyboard.rs b/kernel-rs/src/keyboard.rs
new file mode 100644
index 00000000..75787813
--- /dev/null
+++ b/kernel-rs/src/keyboard.rs
@@ -0,0 +1,145 @@
+use console;
+use x86::devices::io::{Io, Pio};
+
+const MAX_KEYS: usize = 59;
+const KEYMAP_US: [[u8; 2]; MAX_KEYS] = [
+ *b"\0\0",
+ *b"\0\0",//escape
+ *b"1!",
+ *b"2@",
+ *b"3#",
+ *b"4$",
+ *b"5%",
+ *b"6^",
+ *b"7&",
+ *b"8*",
+ *b"9(",
+ *b"0)",
+ *b"-_",
+ *b"=+",
+ *b"\0\0",//backspace
+ *b"\0\0",//tab
+ *b"qQ",
+ *b"wW",
+ *b"eE",
+ *b"rR",
+ *b"tT",
+ *b"yY",
+ *b"uU",
+ *b"iI",
+ *b"oO",
+ *b"pP",
+ *b"[{",
+ *b"]}",
+ *b"\n\n",
+ *b"\0\0",//left_control
+ *b"aA",
+ *b"sS",
+ *b"dD",
+ *b"fF",
+ *b"gG",
+ *b"hH",
+ *b"jJ",
+ *b"kK",
+ *b"lL",
+ *b";:",
+ *b"'\"",
+ *b"`~",
+ *b"\0\0",//left shift
+ *b"\\|",
+ *b"zZ",
+ *b"xX",
+ *b"cC",
+ *b"vV",
+ *b"bB",
+ *b"nN",
+ *b"mM",
+ *b",<",
+ *b".>",
+ *b"/?",
+ *b"\0\0",//right shift
+ *b"**",
+ *b"\0\0",//left alt
+ *b" ",
+ *b"\0\0",//capslock
+ ];
+
+pub static mut PS2: Ps2 = Ps2::new();
+
+pub struct Ps2 {
+ status: Pio,
+ data: Pio,
+}
+
+impl Ps2 {
+ pub const fn new() -> Ps2 {
+ Ps2 {
+ status: Pio::new(0x64),
+ data: Pio::new(0x60),
+ }
+ }
+
+ pub fn clear_buffer(&self) {
+ let mut buffer: u8 = 0x02;
+ while buffer & 0x02 != 0 {
+ self.data.read();
+ buffer = self.status.read();
+ }
+ }
+
+ pub fn ps2_8042_reset(&mut self) {
+ use x86::instructions::interrupts;
+ interrupts::disable();
+ self.clear_buffer();
+ self.status.write(0xFE);
+ }
+
+ pub fn get_scancode(&self) -> u8 {
+ let mut scancode = 0;
+ loop {
+ if self.data.read() != scancode {
+ scancode = self.data.read();
+ if scancode > 0 {
+ return scancode;
+ }
+ }
+ }
+ }
+}
+
+const TOUCH_RELEASE: u8 = 1 << 7;
+
+fn check_key_state(key: u8) -> (bool, usize) {
+ if (key & TOUCH_RELEASE) == TOUCH_RELEASE {
+ (true, (key - TOUCH_RELEASE) as usize)
+ } else {
+ (false, key as usize)
+ }
+}
+
+pub fn kbd_callback() {
+ static mut SHIFT: bool = false;
+ static mut CTRL: bool = false;
+ static mut ALT: bool = false;
+ let scancode = unsafe { PS2.get_scancode() };
+ let (is_release, scancode) = check_key_state(scancode);
+ unsafe {
+ //TODO remove unsafe
+ match self::KEYMAP_US.get(scancode as usize) {
+ Some(b"\0\0") => match scancode {
+ 0x2A | 0x36 => SHIFT = !is_release,
+ 0x38 => ALT = !is_release,
+ 0x1D => CTRL = !is_release,
+ 0x0E if !is_release => {
+ console::CONSOLE.backspace();
+ }
+ _ => {}
+ },
+ Some(ascii) if !is_release => {
+ let sym = if SHIFT { ascii[1] } else { ascii[0] };
+ console::CONSOLE.keypress(sym);
+ }
+ _ => {}
+ }
+ }
+}
diff --git a/kernel-rs/src/lib.rs b/kernel-rs/src/lib.rs
new file mode 100644
index 00000000..c363c8a5
--- /dev/null
+++ b/kernel-rs/src/lib.rs
@@ -0,0 +1,79 @@
+//! project hosted on [github](https://github.com/jzck/kernel)
+
+// nightly stuff we need
+#![no_std]
+#![feature(lang_items)]
+#![feature(naked_functions)]
+#![feature(const_fn)]
+#![feature(ptr_internals)]
+#![feature(asm)]
+#![feature(thread_local)]
+// home made heap
+#![feature(alloc)]
+#![feature(alloc_error_handler)]
+#![feature(allocator_api)]
+// x86 specific
+#![feature(abi_x86_interrupt)]
+
+// extern crate core;
+extern crate alloc;
+#[macro_use]
+extern crate lazy_static;
+extern crate multiboot2;
+extern crate raw_cpuid;
+extern crate rlibc;
+extern crate slab_allocator;
+extern crate spin;
+
+extern crate bitflags;
+
+// used by arch/x86, need conditional compilation here
+extern crate x86;
+
+#[macro_use]
+pub mod vga;
+pub mod keyboard;
+pub mod console;
+pub mod acpi;
+pub mod allocator;
+pub mod memory;
+pub mod arch;
+pub use arch::x86::consts::*;
+pub mod scheduling;
+#[allow(dead_code)]
+pub mod time;
+pub mod pci;
+
+/// kernel entry point.
+/// ::arch is responsible for calling this once the core has loaded.
+pub fn kmain() -> ! {
+ // memory init after heap is available
+ // memory::init_noncore();
+
+ // unsafe VGA
+ unsafe { console::CONSOLE.init(); }
+
+ if let Some(ide) = pci::get_first(0x1, 0x1) {
+ println!("found IDE at slot {}", ide.slot);
+ pci::ide::init(ide);
+ }
+
+ // scheduler WIP
+ // scheduling::schedule();
+ unreachable!();
+}
+
+#[lang = "eh_personality"]
+#[no_mangle]
+pub extern "C" fn eh_personality() {}
+
+#[panic_handler]
+#[no_mangle]
+pub extern "C" fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
+ println!("{}", info);
+ flush!();
+ loop {}
+}
+
+#[global_allocator]
+pub static ALLOCATOR: slab_allocator::LockedHeap = slab_allocator::LockedHeap::empty();
diff --git a/kernel-rs/src/memory/bump.rs b/kernel-rs/src/memory/bump.rs
new file mode 100644
index 00000000..fce35247
--- /dev/null
+++ b/kernel-rs/src/memory/bump.rs
@@ -0,0 +1,115 @@
+use multiboot2::{MemoryArea, MemoryAreaIter};
+use x86::*;
+use x86::structures::paging::PhysFrame;
+use super::FrameAllocator;
+
+pub struct BumpFrameAllocator {
+ next_free_frame: PhysFrame,
+ current_area: Option<&'static MemoryArea>,
+ areas: MemoryAreaIter,
+ kernel_start: PhysFrame,
+ kernel_end: PhysFrame,
+ multiboot_start: PhysFrame,
+ multiboot_end: PhysFrame,
+}
+
+impl BumpFrameAllocator {
+ pub fn new(
+ kernel_start: usize,
+ kernel_end: usize,
+ multiboot_start: usize,
+ multiboot_end: usize,
+ memory_areas: MemoryAreaIter,
+ ) -> BumpFrameAllocator {
+ let mut allocator = BumpFrameAllocator {
+ next_free_frame: PhysFrame { number: 0 },
+ current_area: None,
+ areas: memory_areas,
+ kernel_start: PhysFrame::containing_address(PhysAddr::new(kernel_start as u32)),
+ kernel_end: PhysFrame::containing_address(PhysAddr::new(kernel_end as u32)),
+ multiboot_start: PhysFrame::containing_address(PhysAddr::new(multiboot_start as u32)),
+ multiboot_end: PhysFrame::containing_address(PhysAddr::new(multiboot_end as u32)),
+ };
+ allocator.choose_next_area();
+ allocator
+ }
+
+ fn choose_next_area(&mut self) {
+ // get next area with free frames
+ self.current_area = self.areas
+ .clone()
+ .filter(|area| {
+ area.end_address() >= self.next_free_frame.start_address().as_u32() as usize
+ })
+ .min_by_key(|area| area.start_address());
+
+ if let Some(area) = self.current_area {
+ let start_frame =
+ PhysFrame::containing_address(PhysAddr::new(area.start_address() as u32));
+ if self.next_free_frame < start_frame {
+ self.next_free_frame = start_frame;
+ }
+ }
+ }
+}
+
+impl FrameAllocator for BumpFrameAllocator {
+ fn allocate_frames(&mut self, count: usize) -> Option {
+ if count == 0 {
+ return None;
+ };
+ // println!("allocate {}", count);
+ // println!("kstart {:?}", self.kernel_start);
+ // println!("kend {:?}", self.kernel_end);
+ // println!("multiboot start {:?}", self.multiboot_start);
+ // println!("multiboot end {:?}", self.multiboot_end);
+ // flush!();
+ if let Some(area) = self.current_area {
+ let start_frame = PhysFrame {
+ number: self.next_free_frame.number,
+ };
+ let end_frame = PhysFrame {
+ number: self.next_free_frame.number + count as u32 - 1,
+ };
+
+ let current_area_last_frame =
+ PhysFrame::containing_address(PhysAddr::new(area.end_address() as u32));
+
+ if end_frame > current_area_last_frame {
+ // all frames are taken in this 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 used by kernel
+ self.next_free_frame = PhysFrame {
+ number: self.kernel_end.number + 1,
+ };
+ } else if (start_frame >= self.multiboot_start && start_frame <= self.multiboot_end)
+ || (end_frame >= self.multiboot_start && end_frame <= self.multiboot_end)
+ {
+ // frame used by multiboot
+ self.next_free_frame = PhysFrame {
+ number: self.multiboot_end.number + 1,
+ };
+ } else {
+ self.next_free_frame.number += count as u32;
+ return Some(start_frame);
+ }
+ // try again with next_free_frame
+ self.allocate_frames(count)
+ } else {
+ None
+ }
+ }
+
+ fn deallocate_frames(&mut self, frame: PhysFrame, count: usize) {
+ // bump doesnt deallocate, must be used inside of a recycler
+ println!(
+ "lost frames {:#x} ({})",
+ frame.start_address().as_u32(),
+ count
+ );
+ // unimplemented!();
+ }
+}
diff --git a/kernel-rs/src/memory/mod.rs b/kernel-rs/src/memory/mod.rs
new file mode 100644
index 00000000..2279c3e8
--- /dev/null
+++ b/kernel-rs/src/memory/mod.rs
@@ -0,0 +1,163 @@
+mod bump;
+mod recycle;
+// mod stack_allocator;
+
+use multiboot2;
+use x86::structures::paging::*;
+
+use self::bump::BumpFrameAllocator;
+use self::recycle::RecycleAllocator;
+// use self::stack_allocator::{Stack, StackAllocator};
+
+pub trait FrameAllocator {
+ fn allocate_frames(&mut self, size: usize) -> Option;
+ fn deallocate_frames(&mut self, frame: PhysFrame, size: usize);
+}
+
+pub struct MemoryControler {
+ frame_allocator: RecycleAllocator,
+ // stack_allocator: StackAllocator,
+}
+
+static mut MEMORY_CONTROLER: Option = None;
+
+pub fn init(boot_info: &multiboot2::BootInformation) {
+ let elf_sections_tag = boot_info.elf_sections_tag().unwrap();
+ let memory_map_tag = boot_info.memory_map_tag().unwrap();
+
+ let kernel_start = elf_sections_tag
+ .sections()
+ .filter(|s| s.is_allocated())
+ .map(|s| s.start_address())
+ .min()
+ .unwrap();
+
+ let kernel_end = elf_sections_tag
+ .sections()
+ .filter(|s| s.is_allocated())
+ .map(|s| s.start_address() + s.size())
+ .max()
+ .unwrap();
+
+ let bump_allocator = BumpFrameAllocator::new(
+ kernel_start as usize,
+ kernel_end as usize,
+ boot_info.start_address(),
+ boot_info.end_address(),
+ memory_map_tag.memory_areas(),
+ );
+
+ let frame_allocator = RecycleAllocator::new(bump_allocator);
+ // let stack_allocator = StackAllocator::new(::USER_STACK_RANGE);
+
+ unsafe {
+ MEMORY_CONTROLER = Some(MemoryControler {
+ frame_allocator,
+ // stack_allocator,
+ });
+ }
+}
+
+pub fn allocate_frames(count: usize) -> Option {
+ unsafe {
+ if let Some(ref mut controler) = MEMORY_CONTROLER {
+ controler.frame_allocator.allocate_frames(count)
+ } else {
+ panic!("frame allocator not initialized!");
+ }
+ }
+}
+
+pub fn deallocate_frames(frame: PhysFrame, count: usize) {
+ unsafe {
+ if let Some(ref mut controler) = MEMORY_CONTROLER {
+ controler.frame_allocator.deallocate_frames(frame, count)
+ } else {
+ panic!("frame allocator not initialized!");
+ }
+ }
+}
+
+// pub fn allocate_stack(mut active_table: &mut ActivePageTable) -> Option {
+// unsafe {
+// if let Some(ref mut controler) = MEMORY_CONTROLER {
+// controler
+// .stack_allocator
+// .allocate_stack(&mut active_table, 4)
+// } else {
+// panic!("frame allocator not initialized!");
+// }
+// }
+// }
+
+/// Init memory module after core
+/// Must be called once, and only once,
+pub fn init_noncore() {
+ unsafe {
+ if let Some(ref mut controler) = MEMORY_CONTROLER {
+ controler.frame_allocator.set_core(true);
+ } else {
+ panic!("frame allocator not initialized");
+ }
+ }
+}
+
+
+/// for console
+#[allow(unconditional_recursion)]
+pub fn overflow() {
+ overflow();
+}
+
+/// for console
+pub fn page_fault() {
+ unsafe {
+ *(0xdead as *mut u32) = 42;
+ };
+}
+
+/// Print the kernel stack
+pub fn print_stack() {
+ fn hexdump(start: usize, end: usize) {
+ let mut address = 0;
+ let data = unsafe { core::slice::from_raw_parts_mut(start as *mut u8, end - start) };
+ while address <= data.len() {
+ let next_end = core::cmp::min(address + 16, data.len());
+ print_line(&data[address..next_end], address + start);
+ address = address + 16;
+ }
+ println!("");
+ }
+
+ fn is_control(c: char) -> bool {
+ !(c >= ' ' && c <= '~')
+ }
+
+ fn print_line(line: &[u8], address: usize) {
+ print!("\n{:#08x}: ", address);
+ for byte in line {
+ print!("{:02x} ", *byte);
+ }
+ let length: usize = 16 - line.len();
+ for _ in 0..length {
+ print!(" ");
+ }
+ print!("|");
+ for byte in line {
+ match is_control(*byte as char) {
+ true => print!("."),
+ false => print!("{}", *byte as char),
+ };
+ }
+ print!("|");
+ }
+
+ let esp: usize;
+ let ebp: usize;
+ unsafe { asm!("" : "={esp}"(esp), "={ebp}"(ebp):::) };
+ println!("esp = {:#x}", esp);
+ println!("ebp = {:#x}", ebp);
+ println!("size = {:#X} bytes", ebp - esp);
+ hexdump(esp, ebp);
+ flush!();
+}
diff --git a/kernel-rs/src/memory/recycle.rs b/kernel-rs/src/memory/recycle.rs
new file mode 100644
index 00000000..4772addb
--- /dev/null
+++ b/kernel-rs/src/memory/recycle.rs
@@ -0,0 +1,105 @@
+//! Recycle allocator
+//! Uses freed frames if possible, then uses inner allocator
+
+use alloc::vec::Vec;
+use x86::*;
+use x86::structures::paging::*;
+use super::*;
+
+pub struct RecycleAllocator {
+ inner: T,
+ core: bool,
+ free: Vec<(usize, usize)>,
+}
+
+impl RecycleAllocator {
+ pub fn new(inner: T) -> Self {
+ Self {
+ inner: inner,
+ core: true,
+ free: Vec::new(),
+ }
+ }
+
+ pub fn set_core(&mut self, core: bool) {
+ self.core = core;
+ }
+
+ 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 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(PhysFrame::containing_address(PhysAddr::new(address as u32)))
+ } else {
+ //println!("No saved frames {}", count);
+ self.inner.allocate_frames(count)
+ }
+ }
+
+ fn deallocate_frames(&mut self, frame: PhysFrame, count: usize) {
+ // we cant use vec! before the heap has been initialized
+ if self.core {
+ self.inner.deallocate_frames(frame, count);
+ } else {
+ let address = frame.start_address().as_u32() as usize;
+ if !self.merge(address, count) {
+ self.free.push((address, count));
+ }
+ }
+ }
+}
diff --git a/kernel-rs/src/memory/stack_allocator.rs b/kernel-rs/src/memory/stack_allocator.rs
new file mode 100644
index 00000000..710932d9
--- /dev/null
+++ b/kernel-rs/src/memory/stack_allocator.rs
@@ -0,0 +1,77 @@
+use x86::structures::paging::*;
+use arch::x86::paging::ActivePageTable;
+use memory::*;
+use core::ops::Range;
+
+#[derive(Debug)]
+pub struct Stack {
+ pub top: u32,
+ pub bottom: u32,
+}
+
+impl Stack {
+ fn new(top: u32, bottom: u32) -> Stack {
+ assert!(top > bottom);
+ Stack { top, bottom }
+ }
+
+ pub fn top(&self) -> u32 {
+ self.top
+ }
+
+ pub fn bottom(&self) -> u32 {
+ self.bottom
+ }
+}
+
+#[derive(Debug)]
+pub struct StackAllocator {
+ range: Range,
+}
+
+impl StackAllocator {
+ pub fn new(range: Range) -> StackAllocator {
+ StackAllocator { range }
+ }
+
+ pub fn allocate_stack(
+ &mut self,
+ active_table: &mut ActivePageTable,
+ size_in_pages: usize,
+ ) -> Option {
+ if size_in_pages == 0 {
+ return None; /* a zero sized stack makes no sense */
+ }
+
+ // clone the range, since we only want to change it on success
+ let mut range = self.range.clone();
+
+ // try to allocate the stack pages and a guard page
+ let guard_page = range.next();
+ let stack_start = range.next();
+ let stack_end = if size_in_pages == 1 {
+ stack_start
+ } else {
+ // choose the (size_in_pages-2)th element, since index
+ // starts at 0 and we already allocated the start page
+ range.nth(size_in_pages - 2)
+ };
+
+ match (guard_page, stack_start, stack_end) {
+ (Some(_), Some(start), Some(end)) => {
+ // success! write back updated range
+ self.range = range.clone();
+
+ // map stack pages to physical frames
+ for page in range {
+ active_table.map(page, PageTableFlags::WRITABLE);
+ }
+
+ // create a new stack
+ let top_of_stack = end.start_address().as_u32() + PAGE_SIZE as u32;
+ Some(Stack::new(top_of_stack, start.start_address().as_u32()))
+ }
+ _ => None, /* not enough pages */
+ }
+ }
+}
diff --git a/kernel-rs/src/pci/ide.rs b/kernel-rs/src/pci/ide.rs
new file mode 100644
index 00000000..f817db3e
--- /dev/null
+++ b/kernel-rs/src/pci/ide.rs
@@ -0,0 +1,4 @@
+use super::*;
+
+pub fn init(drive: Pci) {
+}
diff --git a/kernel-rs/src/pci/mod.rs b/kernel-rs/src/pci/mod.rs
new file mode 100644
index 00000000..20652210
--- /dev/null
+++ b/kernel-rs/src/pci/mod.rs
@@ -0,0 +1,126 @@
+// https://wiki.osdev.org/PCI
+use x86::devices::io::{Pio, Io};
+use alloc::vec::*;
+
+pub mod ide;
+
+pub static mut PCI_CONFIG_ADDRESS: Pio = Pio::new(0xCF8);
+pub static mut PCI_CONFIG_DATA: Pio = Pio::new(0xCFC);
+
+pub struct Pci {
+ pub bus: u32,
+ pub slot: u32,
+ pub func: u32,
+ pub device: u16,
+ pub vendor: u16,
+ pub class: u8,
+ pub subclass: u8,
+ pub header_type: u8,
+}
+
+pub fn lspci() {
+ for pci in self::get_all() {
+ println!("{}", pci);
+ }
+}
+
+pub fn get_all() -> Vec {
+ let bus = 0;
+ let mut slot = 0;
+ let mut pcis: Vec = Vec::new();
+
+ while let Some(pci)= pci_get(bus, slot, 0) {
+ if pci.header_type & 0x80 != 0 {
+ let mut func = 0;
+ while let Some(pci)= pci_get(bus, slot, func) {
+ // device has multiple functions
+ pcis.push(pci);
+ func += 1;
+ if func == 7 {break;}
+ }
+ }
+ pcis.push(pci);
+ slot += 1;
+ if slot == 32 {break;}
+ }
+ pcis
+}
+
+pub fn get_first(class: u8, subclass: u8) -> Option {
+ let bus = 0;
+ let mut slot = 0;
+
+ while let Some(pci)= pci_get(bus, slot, 0) {
+ if pci.class == class && pci.subclass == subclass {
+ return Some(pci);
+ }
+ if pci.header_type & 0x80 != 0 {
+ let mut func = 0;
+ while let Some(pci)= pci_get(bus, slot, func) {
+ // device has multiple functions
+ if pci.class == class && pci.subclass == subclass {
+ return Some(pci);
+ }
+ func += 1;
+ if func == 7 {break;}
+ }
+ }
+ slot += 1;
+ if slot == 32 {break;}
+ }
+ None
+}
+
+pub fn pci_get(bus: u32, slot: u32, func: u32) -> Option {
+ let vendor = pci_config_read_word(bus, slot, func, 0);
+ if vendor == 0xffff { return None }
+ let pci = Pci {bus, slot, func, vendor,
+ device: pci_config_read_word(bus, slot, func, 2),
+ subclass: pci_config_read_byte(bus, slot, func, 10),
+ class: pci_config_read_byte(bus, slot, func, 11),
+ header_type: pci_config_read_byte(bus, slot, func, 14),
+ };
+ Some(pci)
+}
+
+use core::fmt;
+impl fmt::Display for Pci {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> fmt::Result {
+ write!(f, "{}:{}.{} {:#x},{:#x} {:#x} {:#x}",
+ self.bus, self.slot, self.func,
+ self.class, self.subclass,
+ self.vendor, self.device);
+ Ok(())
+ }
+}
+
+// pub fn pci_display(bus: u32, slot: u32, function: u32) {
+// let vendor = pci_config_read_word(bus, slot, function, 0);
+// let device = pci_config_read_word(bus, slot, function, 2);
+// println!("{}:{}.{} {:#x},{:#x}: {:#x} {:#x}",
+// bus, slot, function, class, subclass, vendor, device);
+// }
+
+pub fn pci_access(bus: u32, slot: u32, func: u32, offset: u32) {
+ let address = (bus as u32) << 16
+ | (slot as u32) << 11
+ | (func as u32) << 8
+ | (offset as u32) & 0xfc
+ | 0x80000000;
+ unsafe { PCI_CONFIG_ADDRESS.write(address); }
+}
+
+pub fn pci_config_read_doubleword(bus: u32, slot: u32, func: u32, offset: u32) -> u32 {
+ pci_access(bus, slot, func, offset);
+ unsafe { PCI_CONFIG_DATA.read() }
+}
+
+pub fn pci_config_read_word(bus: u32, slot: u32, func: u32, offset: u32) -> u16 {
+ pci_access(bus, slot, func, offset);
+ unsafe { (PCI_CONFIG_DATA.read() >> ((offset & 2) * 8)) as u16 }
+}
+
+pub fn pci_config_read_byte(bus: u32, slot: u32, func: u32, offset: u32) -> u8 {
+ pci_access(bus, slot, func, offset);
+ unsafe { (PCI_CONFIG_DATA.read() >> ((offset & 3) * 8)) as u8 }
+}
diff --git a/kernel-rs/src/scheduling/fifo.rs b/kernel-rs/src/scheduling/fifo.rs
new file mode 100644
index 00000000..aea1e4bb
--- /dev/null
+++ b/kernel-rs/src/scheduling/fifo.rs
@@ -0,0 +1,36 @@
+//! simple first come first serve scheduling algorithm
+//! unsound for everyday use, a process can decide to
+//! hijack the cpu, also it only allows for terminating
+//! processes...
+//! however it's stupid simple to implement!
+
+use alloc::collections::vec_deque::VecDeque;
+
+use super::*;
+// use super::process::*;
+
+pub struct Fifo {
+ list: VecDeque,
+ next_pid: i32,
+}
+
+impl Fifo {
+ pub fn new() -> Fifo {
+ Fifo {
+ list: VecDeque::new(),
+ next_pid: 1,
+ }
+ }
+}
+
+impl Scheduler for Fifo {
+ fn add_task(&mut self, ip: u32) {
+ let p = Process::new(self.next_pid, ip);
+ self.list.push_back(p);
+ self.next_pid += 1;
+ }
+
+ fn next(&mut self) -> Option {
+ self.list.pop_front()
+ }
+}
diff --git a/kernel-rs/src/scheduling/mod.rs b/kernel-rs/src/scheduling/mod.rs
new file mode 100644
index 00000000..0c05f51e
--- /dev/null
+++ b/kernel-rs/src/scheduling/mod.rs
@@ -0,0 +1,56 @@
+mod process;
+mod sleep;
+
+mod fifo;
+
+use spin::Mutex;
+pub use self::process::*;
+lazy_static! {
+ pub static ref SCHEDULER: Mutex = Mutex::new({
+ let init_process: u32 = self::init as *const () as u32;
+ let mut f = fifo::Fifo::new();
+ f.add_task(init_process);
+ f
+ });
+}
+
+/// Scheduler algorithm needs to implement this
+pub trait Scheduler {
+ fn add_task(&mut self, ip: u32);
+ fn next(&mut self) -> Option;
+}
+
+pub fn schedule() {
+ loop {
+ if let Some(mut p) = SCHEDULER.lock().next() {
+ println!("executing {:#?}", p);
+ flush!();
+ unsafe {
+ SCHEDULER.force_unlock();
+ p.execute();
+ }
+ unreachable!();
+ }
+ }
+}
+
+pub fn fork() -> i32 {
+ let ip;
+ unsafe { asm!("pop $0" : "=r"(ip) ::: "intel") };
+ println!("ip = {:#x}", ip);
+ flush!();
+ unsafe { asm!("push $0" :: "r"(ip) :: "intel") };
+ SCHEDULER.lock().add_task(ip);
+ 0
+}
+
+pub fn sleep() {
+
+}
+
+pub fn init() {
+ fprintln!("init first line");
+ // let i = self::fork();
+ // println!("fork={}", i);
+ fprintln!("init last line");
+}
diff --git a/kernel-rs/src/scheduling/process.rs b/kernel-rs/src/scheduling/process.rs
new file mode 100644
index 00000000..e09ac959
--- /dev/null
+++ b/kernel-rs/src/scheduling/process.rs
@@ -0,0 +1,33 @@
+#[derive(Debug)]
+pub enum State {
+ Running,
+ Ready,
+ Sleeping(u32),
+ Blocked(),
+}
+
+#[derive(Debug)]
+pub struct Process {
+ pid: i32,
+ // this is eip right now
+ // this will be an elf blob later
+ ip: u32,
+ state: State,
+}
+
+impl Process {
+ pub fn new(pid: i32, ip: u32) -> Process {
+ Process {
+ pid,
+ ip,
+ state: State::Ready,
+ }
+ }
+
+ pub unsafe fn execute(&mut self) {
+ use super::schedule;
+ let scheduler_loop = schedule as *const () as u32;
+ asm!("push $0; push $1; ret" :: "r"(scheduler_loop) ,"r"(self.ip) :: "volatile", "intel");
+ unreachable!();
+ }
+}
diff --git a/kernel-rs/src/scheduling/sleep.rs b/kernel-rs/src/scheduling/sleep.rs
new file mode 100644
index 00000000..ca453133
--- /dev/null
+++ b/kernel-rs/src/scheduling/sleep.rs
@@ -0,0 +1,41 @@
+//! sleeping processing are stored in a delta queue
+//! separate from other scheduling structures: this
+//! way the scheduling algorithms don't have to worry about
+//! managing these
+//!
+//! https://wiki.osdev.org/Blocking_Process
+
+use alloc::collections::vec_deque::VecDeque;
+use super::*;
+
+struct Sleeper {
+ process: Process,
+ // ms
+ ticks: u32,
+}
+
+/// osdev calls this a delta queue but google doesnt know
+struct DeltaQueue {
+ queue: VecDeque,
+}
+
+impl DeltaQueue {
+ pub fn insert(&mut self, process: Process, ticks: u32) {
+ let sleeper = Sleeper { process, ticks };
+ }
+
+ /// decreases timer on the list and returns the number
+ /// of finished sleepers
+ pub fn tick(&mut self) -> u32 {
+ let mut i: u32 = 0;
+ while let Some(link) = self.queue.get_mut(i as usize) {
+ // find how many links have 0 ticks left
+ if link.ticks > 0 {
+ link.ticks -= 1;
+ break;
+ }
+ i += 1;
+ }
+ i
+ }
+}
diff --git a/kernel-rs/src/time.rs b/kernel-rs/src/time.rs
new file mode 100644
index 00000000..ce8cc0aa
--- /dev/null
+++ b/kernel-rs/src/time.rs
@@ -0,0 +1,22 @@
+use spin::Mutex;
+
+/// Kernel start time, measured in (seconds, microseconds) since Unix epoch
+pub static START: Mutex<(u32, u32)> = Mutex::new((0, 0));
+/// Kernel up time, measured in (seconds, microseconds) since `START_TIME`
+pub static OFFSET: Mutex<(u32, u32)> = Mutex::new((0, 0));
+
+pub fn monotonic() -> (u32, u32) {
+ *OFFSET.lock()
+}
+
+pub fn realtime() -> (u32, u32) {
+ let offset = monotonic();
+ let start = *START.lock();
+ let sum = start.1 + offset.1;
+ (start.0 + offset.0 + sum / 1_000_000, sum % 1_000_000)
+}
+
+pub fn uptime() {
+ let offset = self::OFFSET.lock();
+ println!("{}s", offset.0 + offset.1 / 1_000_000);
+}
diff --git a/kernel-rs/src/vga/color.rs b/kernel-rs/src/vga/color.rs
new file mode 100644
index 00000000..91bf39c4
--- /dev/null
+++ b/kernel-rs/src/vga/color.rs
@@ -0,0 +1,30 @@
+#[allow(dead_code)]
+#[derive(Debug, Clone, Copy)]
+#[repr(u8)]
+pub enum Color {
+ Black = 0,
+ Blue = 1,
+ Green = 2,
+ Cyan = 3,
+ Red = 4,
+ Magenta = 5,
+ Brown = 6,
+ LightGray = 7,
+ DarkGray = 8,
+ LightBlue = 9,
+ LightGreen = 10,
+ LightCyan = 11,
+ LightRed = 12,
+ Pink = 13,
+ Yellow = 14,
+ White = 15,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct ColorCode(pub u8);
+
+impl ColorCode {
+ pub const fn new(foreground: Color, background: Color) -> ColorCode {
+ ColorCode((background as u8) << 4 | (foreground as u8))
+ }
+}
diff --git a/kernel-rs/src/vga/cursor.rs b/kernel-rs/src/vga/cursor.rs
new file mode 100644
index 00000000..575e467f
--- /dev/null
+++ b/kernel-rs/src/vga/cursor.rs
@@ -0,0 +1,30 @@
+// https://wiki.osdev.org/Text_Mode_Cursor
+// Protected mode cursor abstraction
+
+use x86::devices::io::{Io, Pio};
+
+pub static mut CURSOR: Cursor = Cursor::new(0x3D4);
+
+pub struct Cursor {
+ cmd: Pio,
+ data: Pio,
+}
+
+impl Cursor {
+ pub const fn new(port: u16) -> Cursor {
+ Cursor {
+ cmd: Pio::new(port),
+ data: Pio::new(port + 1),
+ }
+ }
+
+ pub fn flush(&mut self, position: usize) {
+ // 14 awaits the rightmost 8bits
+ self.cmd.write(14);
+ self.data.write((position >> 8) as u8 & 0xff);
+
+ // 15 awaits the leftmost 8bits
+ self.cmd.write(15);
+ self.data.write((position >> 0) as u8 & 0xff);
+ }
+}
diff --git a/kernel-rs/src/vga/mod.rs b/kernel-rs/src/vga/mod.rs
new file mode 100644
index 00000000..2d8834ab
--- /dev/null
+++ b/kernel-rs/src/vga/mod.rs
@@ -0,0 +1,160 @@
+pub mod color;
+pub mod cursor;
+
+pub use self::color::{Color, ColorCode};
+use self::cursor::CURSOR;
+
+pub static mut VGA: Writer = self::Writer::new();
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+struct ScreenChar {
+ ascii_character: u8,
+ color_code: ColorCode,
+}
+
+// print wrapper macro around vga
+#[allow(unused_macros)]
+macro_rules! print {
+ ($($arg:tt)*) => ({
+ $crate::vga::print(format_args!($($arg)*));
+ });
+}
+
+// flushed print
+#[allow(unused_macros)]
+macro_rules! fprint {
+ ($($arg:tt)*) => ({
+ print!($($arg)*);
+ flush!();
+ });
+}
+
+// print with a line feed
+#[allow(unused_macros)]
+macro_rules! println {
+ () => ({print!("\n")});
+ ($fmt:expr) => (print!(concat!($fmt, "\n")));
+ ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
+}
+
+// flushed println
+#[allow(unused_macros)]
+macro_rules! fprintln {
+ ($($arg:tt)*) => ({
+ println!($($arg)*);
+ flush!();
+ });
+}
+
+macro_rules! flush {
+ () => (#[allow(unused_unsafe)] unsafe { $crate::vga::VGA.flush() });
+}
+
+macro_rules! set_color {
+ () => (unsafe { $crate::vga::VGA.color_code =
+ $crate::vga::ColorCode::new($crate::vga::Color::White, $crate::vga::Color::Black)} );
+ ($fg:ident) => (unsafe { $crate::vga::VGA.color_code =
+ $crate::vga::ColorCode::new($crate::vga::Color::$fg, $crate::vga::Color::Black)} );
+ ($fg:ident, $bg:ident) => (unsafe { $crate::vga::VGA.color_code =
+ $crate::vga::ColorCode::new($crate::vga::Color::$fg, $crate::vga::Color::$bg)} );
+}
+
+pub fn print(args: fmt::Arguments) {
+ use core::fmt::Write;
+ unsafe {
+ self::VGA.write_fmt(args).unwrap();
+ }
+}
+
+const BUFFER_ROWS: usize = 25;
+const BUFFER_COLS: usize = 80 * 2;
+
+pub struct Writer {
+ pub buffer_pos: usize,
+ pub color_code: ColorCode,
+ buffer: [u8; BUFFER_ROWS * BUFFER_COLS],
+}
+
+impl Writer {
+ pub const fn new() -> Writer {
+ Writer {
+ buffer_pos: 0,
+ color_code: ColorCode::new(Color::White, Color::Black),
+ buffer: [0; BUFFER_ROWS * BUFFER_COLS],
+ }
+ }
+
+ pub fn erase_byte(&mut self) {
+ self.buffer_pos -= 2;
+ let i = self.buffer_pos;
+ self.buffer[i] = b' ';
+ self.buffer[i + 1] = self.color_code.0;
+ self.flush();
+ }
+
+ pub fn write_byte(&mut self, byte: u8) {
+ match byte {
+ b'\n' => {
+ let current_line = self.buffer_pos / (BUFFER_COLS);
+ self.buffer_pos = (current_line + 1) * BUFFER_COLS;
+ }
+ byte => {
+ self.buffer[self.buffer_pos] = byte;
+ self.buffer[self.buffer_pos + 1] = self.color_code.0;
+ self.buffer_pos += 2;
+ }
+ }
+
+ if self.buffer_pos >= self.buffer.len() {
+ self.scroll();
+ self.flush();
+ }
+ // flushing here is correct but slow
+ // self.flush();
+ }
+
+ pub fn write_str(&mut self, s: &str) {
+ for byte in s.bytes() {
+ self.write_byte(byte)
+ }
+ }
+
+ fn flush_cursor(&self) {
+ unsafe { CURSOR.flush(self.buffer_pos / 2); }
+ }
+
+ pub fn flush(&mut self) {
+ let slice = unsafe { core::slice::from_raw_parts_mut(0xb8000 as *mut u8, 4000) };
+ slice.as_mut().clone_from_slice(&self.buffer);
+ self.flush_cursor();
+ }
+
+ fn scroll(&mut self) {
+ for row in 1..BUFFER_ROWS {
+ for col in 0..BUFFER_COLS {
+ let prev_position = ((row - 1) * BUFFER_COLS) + col;
+ let current_position = (row * BUFFER_COLS) + col;
+ self.buffer[prev_position] = self.buffer[current_position];
+ }
+ }
+
+ for col in (0..BUFFER_COLS / 2).map(|x| x * 2) {
+ self.buffer[((BUFFER_ROWS - 1) * BUFFER_COLS) + (col)] = b' ';
+ self.buffer[((BUFFER_ROWS - 1) * BUFFER_COLS) + (col + 1)] =
+ ColorCode::new(Color::White, Color::Black).0;
+ }
+ self.buffer_pos = (BUFFER_ROWS - 1) * BUFFER_COLS;
+ }
+}
+
+// trait needed by formatting macros
+use core::fmt;
+impl fmt::Write for Writer {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for byte in s.bytes() {
+ self.write_byte(byte)
+ }
+ Ok(())
+ }
+}
diff --git a/kernel-rs/x86 b/kernel-rs/x86
new file mode 160000
index 00000000..229c4acb
--- /dev/null
+++ b/kernel-rs/x86
@@ -0,0 +1 @@
+Subproject commit 229c4acbbe81bf4837bd05714b99614ada9dd2b3
diff --git a/kernel-rs/x86-bluesnow.json b/kernel-rs/x86-bluesnow.json
new file mode 100644
index 00000000..36bdde97
--- /dev/null
+++ b/kernel-rs/x86-bluesnow.json
@@ -0,0 +1,17 @@
+{
+ "arch": "x86",
+
+ "_comment": "http://llvm.org/docs/LangRef.html#data-layout",
+ "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128",
+
+ "llvm-target": "i386-unknown-none",
+ "linker-flavor": "gcc",
+ "os": "none",
+ "target-endian": "little",
+ "target-pointer-width": "32",
+ "target-c-int-width": "32",
+ "features": "-mmx,-fxsr,-sse,-sse2,+soft-float",
+ "eliminate-frame-pointer": false,
+ "disable-redzone": true,
+ "panic-strategy": "abort"
+}