Merge kernel-rs with full history
This commit is contained in:
commit
a0d4344b67
55 changed files with 3503 additions and 0 deletions
3
kernel-rs/.gitignore
vendored
Normal file
3
kernel-rs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
target
|
||||||
|
build
|
||||||
|
Cargo.lock
|
||||||
6
kernel-rs/.gitmodules
vendored
Normal file
6
kernel-rs/.gitmodules
vendored
Normal file
|
|
@ -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
|
||||||
18
kernel-rs/.travis.yml
Normal file
18
kernel-rs/.travis.yml
Normal file
|
|
@ -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 "<meta http-equiv=refresh content=0;url=`echo $TRAVIS_REPO_SLUG | cut -d '/' -f 2`/index.html>" > 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=
|
||||||
28
kernel-rs/Cargo.toml
Normal file
28
kernel-rs/Cargo.toml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
[package]
|
||||||
|
name = "bluesnow"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jack Halford <jack@crans.org>", "William Escande <wescande@student.42.fr>"]
|
||||||
|
|
||||||
|
[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"
|
||||||
46
kernel-rs/Makefile
Normal file
46
kernel-rs/Makefile
Normal file
|
|
@ -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)
|
||||||
48
kernel-rs/README.md
Normal file
48
kernel-rs/README.md
Normal file
|
|
@ -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)
|
||||||
2
kernel-rs/Xargo.toml
Normal file
2
kernel-rs/Xargo.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.x86-bluesnow.dependencies]
|
||||||
|
alloc = {}
|
||||||
8
kernel-rs/mk/grub.mk
Normal file
8
kernel-rs/mk/grub.mk
Normal file
|
|
@ -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
|
||||||
27
kernel-rs/mk/qemu.mk
Normal file
27
kernel-rs/mk/qemu.mk
Normal file
|
|
@ -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
|
||||||
1
kernel-rs/multiboot2-elf64
Submodule
1
kernel-rs/multiboot2-elf64
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 32552947aa30414a96d9a8e291aceaf18320b3ef
|
||||||
98
kernel-rs/src/acpi/dsdt.rs
Normal file
98
kernel-rs/src/acpi/dsdt.rs
Normal file
|
|
@ -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<u32, &'static str> {
|
||||||
|
let dsdt_start = addr + mem::size_of::<ACPISDTHeader>() 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<u16> = 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<u16> = Pio::new(pm1_cnt[1]);
|
||||||
|
pin.write(slp_typ);
|
||||||
|
// cpuio::outw(pm1_cnt[1], slp_typ);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
165
kernel-rs/src/acpi/fadt.rs
Normal file
165
kernel-rs/src/acpi/fadt.rs
Normal file
|
|
@ -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<FADT> = 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<u8> = 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<FADT, &'static str> {
|
||||||
|
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<u32, &'static str> {
|
||||||
|
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<bool, &'static str> {
|
||||||
|
let fadt = is_init()?;
|
||||||
|
let pm1_cnt = get_cnt(fadt);
|
||||||
|
let pin: Pio<u16> = 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<u8> = 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<u8> = 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
167
kernel-rs/src/acpi/mod.rs
Normal file
167
kernel-rs/src/acpi/mod.rs
Normal file
|
|
@ -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<ACPISDTIter, &'static str> {
|
||||||
|
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::<ACPISDTHeader>() as u32,
|
||||||
|
len: (unsafe { (*ptr).length } as usize - mem::size_of::<ACPISDTHeader>())
|
||||||
|
/ ptr_len,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ACPISDTIter {
|
||||||
|
type Item = u32;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
85
kernel-rs/src/acpi/rsdp.rs
Normal file
85
kernel-rs/src/acpi/rsdp.rs
Normal file
|
|
@ -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<RSDP20> = 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<bool, &'static str> {
|
||||||
|
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::<RSDP>()))
|
||||||
|
|| (revision == 2 && check_checksum(addr, mem::size_of::<RSDP20>()))
|
||||||
|
{
|
||||||
|
unsafe { RSDPTR = Some(rsdp_tmp) };
|
||||||
|
return Ok(revision == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err("Not a valid RSD ptr")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_finding() -> Result<bool, &'static str> {
|
||||||
|
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<RSDP20, &'static str> {
|
||||||
|
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<u64, &'static str> {
|
||||||
|
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<u32, &'static str> {
|
||||||
|
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<bool, &'static str> {
|
||||||
|
memory_finding()
|
||||||
|
}
|
||||||
20
kernel-rs/src/acpi/rsdt.rs
Normal file
20
kernel-rs/src/acpi/rsdt.rs
Normal file
|
|
@ -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, &'static str> {
|
||||||
|
ACPISDTIter::new(unsafe { RSDT }, 4)
|
||||||
|
}
|
||||||
22
kernel-rs/src/acpi/xsdt.rs
Normal file
22
kernel-rs/src/acpi/xsdt.rs
Normal file
|
|
@ -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, &'static str> {
|
||||||
|
ACPISDTIter::new(unsafe { XSDT }, 8)
|
||||||
|
}
|
||||||
27
kernel-rs/src/allocator/mod.rs
Normal file
27
kernel-rs/src/allocator/mod.rs
Normal file
|
|
@ -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");
|
||||||
|
}
|
||||||
4
kernel-rs/src/arch/mod.rs
Normal file
4
kernel-rs/src/arch/mod.rs
Normal file
|
|
@ -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;
|
||||||
98
kernel-rs/src/arch/x86/boot.asm
Normal file
98
kernel-rs/src/arch/x86/boot.asm
Normal file
|
|
@ -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
|
||||||
52
kernel-rs/src/arch/x86/consts.rs
Normal file
52
kernel-rs/src/arch/x86/consts.rs
Normal file
|
|
@ -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<Page> = 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<Page> = USER_STACK_START..USER_STACK_END;
|
||||||
286
kernel-rs/src/arch/x86/devices/cpu.rs
Normal file
286
kernel-rs/src/arch/x86/devices/cpu.rs
Normal file
|
|
@ -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!("");
|
||||||
|
}
|
||||||
|
}
|
||||||
15
kernel-rs/src/arch/x86/devices/mod.rs
Normal file
15
kernel-rs/src/arch/x86/devices/mod.rs
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
64
kernel-rs/src/arch/x86/gdt.rs
Normal file
64
kernel-rs/src/arch/x86/gdt.rs
Normal file
|
|
@ -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!();
|
||||||
|
}
|
||||||
7
kernel-rs/src/arch/x86/grub.cfg
Normal file
7
kernel-rs/src/arch/x86/grub.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
set timeout=0
|
||||||
|
set default=0
|
||||||
|
|
||||||
|
menuentry "Blue Snow" {
|
||||||
|
multiboot2 /boot/bluesnow
|
||||||
|
boot
|
||||||
|
}
|
||||||
52
kernel-rs/src/arch/x86/idt.rs
Normal file
52
kernel-rs/src/arch/x86/idt.rs
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
80
kernel-rs/src/arch/x86/interrupt/exception.rs
Normal file
80
kernel-rs/src/arch/x86/interrupt/exception.rs
Normal file
|
|
@ -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, {});
|
||||||
80
kernel-rs/src/arch/x86/interrupt/irq.rs
Normal file
80
kernel-rs/src/arch/x86/interrupt/irq.rs
Normal file
|
|
@ -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, {});
|
||||||
4
kernel-rs/src/arch/x86/interrupt/mod.rs
Normal file
4
kernel-rs/src/arch/x86/interrupt/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#[macro_use]
|
||||||
|
pub mod exception;
|
||||||
|
#[macro_use]
|
||||||
|
pub mod irq;
|
||||||
80
kernel-rs/src/arch/x86/linker.ld
Normal file
80
kernel-rs/src/arch/x86/linker.ld
Normal file
|
|
@ -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); */
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
141
kernel-rs/src/arch/x86/mod.rs
Normal file
141
kernel-rs/src/arch/x86/mod.rs
Normal file
|
|
@ -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!();
|
||||||
|
}
|
||||||
16
kernel-rs/src/arch/x86/multiboot_header.asm
Normal file
16
kernel-rs/src/arch/x86/multiboot_header.asm
Normal file
|
|
@ -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:
|
||||||
89
kernel-rs/src/arch/x86/paging/mapper.rs
Normal file
89
kernel-rs/src/arch/x86/paging/mapper.rs
Normal file
|
|
@ -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<PageTable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<PhysAddr> {
|
||||||
|
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<PhysFrame> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
189
kernel-rs/src/arch/x86/paging/mod.rs
Normal file
189
kernel-rs/src/arch/x86/paging/mod.rs
Normal file
|
|
@ -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<F>(
|
||||||
|
&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
|
||||||
|
}
|
||||||
47
kernel-rs/src/arch/x86/paging/table.rs
Normal file
47
kernel-rs/src/arch/x86/paging/table.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use x86::structures::paging::*;
|
||||||
|
|
||||||
|
pub trait RecTable {
|
||||||
|
fn next_table_address(&self, index: usize) -> Option<u32>;
|
||||||
|
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<u32> {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
44
kernel-rs/src/arch/x86/paging/temporary_page.rs
Normal file
44
kernel-rs/src/arch/x86/paging/temporary_page.rs
Normal file
|
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
kernel-rs/src/arch/x86/start.asm
Normal file
18
kernel-rs/src/arch/x86/start.asm
Normal file
|
|
@ -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
|
||||||
126
kernel-rs/src/console.rs
Normal file
126
kernel-rs/src/console.rs
Normal file
|
|
@ -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<fn(), &'static str> {
|
||||||
|
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)(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
kernel-rs/src/keyboard.rs
Normal file
145
kernel-rs/src/keyboard.rs
Normal file
|
|
@ -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<u8>,
|
||||||
|
data: Pio<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
kernel-rs/src/lib.rs
Normal file
79
kernel-rs/src/lib.rs
Normal file
|
|
@ -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();
|
||||||
115
kernel-rs/src/memory/bump.rs
Normal file
115
kernel-rs/src/memory/bump.rs
Normal file
|
|
@ -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<PhysFrame> {
|
||||||
|
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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
163
kernel-rs/src/memory/mod.rs
Normal file
163
kernel-rs/src/memory/mod.rs
Normal file
|
|
@ -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<PhysFrame>;
|
||||||
|
fn deallocate_frames(&mut self, frame: PhysFrame, size: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemoryControler {
|
||||||
|
frame_allocator: RecycleAllocator<BumpFrameAllocator>,
|
||||||
|
// stack_allocator: StackAllocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut MEMORY_CONTROLER: Option<MemoryControler> = 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<PhysFrame> {
|
||||||
|
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<Stack> {
|
||||||
|
// 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!();
|
||||||
|
}
|
||||||
105
kernel-rs/src/memory/recycle.rs
Normal file
105
kernel-rs/src/memory/recycle.rs
Normal file
|
|
@ -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<T: FrameAllocator> {
|
||||||
|
inner: T,
|
||||||
|
core: bool,
|
||||||
|
free: Vec<(usize, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FrameAllocator> RecycleAllocator<T> {
|
||||||
|
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<T: FrameAllocator> FrameAllocator for RecycleAllocator<T> {
|
||||||
|
fn allocate_frames(&mut self, count: usize) -> Option<PhysFrame> {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
kernel-rs/src/memory/stack_allocator.rs
Normal file
77
kernel-rs/src/memory/stack_allocator.rs
Normal file
|
|
@ -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<Page>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackAllocator {
|
||||||
|
pub fn new(range: Range<Page>) -> StackAllocator {
|
||||||
|
StackAllocator { range }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_stack(
|
||||||
|
&mut self,
|
||||||
|
active_table: &mut ActivePageTable,
|
||||||
|
size_in_pages: usize,
|
||||||
|
) -> Option<Stack> {
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
kernel-rs/src/pci/ide.rs
Normal file
4
kernel-rs/src/pci/ide.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn init(drive: Pci) {
|
||||||
|
}
|
||||||
126
kernel-rs/src/pci/mod.rs
Normal file
126
kernel-rs/src/pci/mod.rs
Normal file
|
|
@ -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<u32> = Pio::new(0xCF8);
|
||||||
|
pub static mut PCI_CONFIG_DATA: Pio<u32> = 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<Pci> {
|
||||||
|
let bus = 0;
|
||||||
|
let mut slot = 0;
|
||||||
|
let mut pcis: Vec<Pci> = 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<Pci> {
|
||||||
|
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<Pci> {
|
||||||
|
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 }
|
||||||
|
}
|
||||||
36
kernel-rs/src/scheduling/fifo.rs
Normal file
36
kernel-rs/src/scheduling/fifo.rs
Normal file
|
|
@ -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<Process>,
|
||||||
|
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<Process> {
|
||||||
|
self.list.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
56
kernel-rs/src/scheduling/mod.rs
Normal file
56
kernel-rs/src/scheduling/mod.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
mod process;
|
||||||
|
mod sleep;
|
||||||
|
|
||||||
|
mod fifo;
|
||||||
|
|
||||||
|
use spin::Mutex;
|
||||||
|
pub use self::process::*;
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SCHEDULER: Mutex<fifo::Fifo> = 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<Process>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
33
kernel-rs/src/scheduling/process.rs
Normal file
33
kernel-rs/src/scheduling/process.rs
Normal file
|
|
@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
41
kernel-rs/src/scheduling/sleep.rs
Normal file
41
kernel-rs/src/scheduling/sleep.rs
Normal file
|
|
@ -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<Sleeper>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
22
kernel-rs/src/time.rs
Normal file
22
kernel-rs/src/time.rs
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
30
kernel-rs/src/vga/color.rs
Normal file
30
kernel-rs/src/vga/color.rs
Normal file
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
30
kernel-rs/src/vga/cursor.rs
Normal file
30
kernel-rs/src/vga/cursor.rs
Normal file
|
|
@ -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<u8>,
|
||||||
|
data: Pio<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
160
kernel-rs/src/vga/mod.rs
Normal file
160
kernel-rs/src/vga/mod.rs
Normal file
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
1
kernel-rs/x86
Submodule
1
kernel-rs/x86
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 229c4acbbe81bf4837bd05714b99614ada9dd2b3
|
||||||
17
kernel-rs/x86-bluesnow.json
Normal file
17
kernel-rs/x86-bluesnow.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue