Merge kernel-rs with full history

This commit is contained in:
Jack 2026-01-29 18:05:36 -03:00
commit a0d4344b67
No known key found for this signature in database
55 changed files with 3503 additions and 0 deletions

3
kernel-rs/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
target
build
Cargo.lock

6
kernel-rs/.gitmodules vendored Normal file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,2 @@
[target.x86-bluesnow.dependencies]
alloc = {}

8
kernel-rs/mk/grub.mk Normal file
View 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
View 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

@ -0,0 +1 @@
Subproject commit 32552947aa30414a96d9a8e291aceaf18320b3ef

View 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
View 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
View 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)
}
}

View 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()
}

View 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)
}

View 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)
}

View 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");
}

View 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;

View 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

View 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;

View 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!("");
}
}

View 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();
}

View 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!();
}

View file

@ -0,0 +1,7 @@
set timeout=0
set default=0
menuentry "Blue Snow" {
multiboot2 /boot/bluesnow
boot
}

View 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();
}

View 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, {});

View 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, {});

View file

@ -0,0 +1,4 @@
#[macro_use]
pub mod exception;
#[macro_use]
pub mod irq;

View 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); */
/* } */
}

View 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!();
}

View 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:

View 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);
}
}

View 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(&section.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
}

View 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")
}
}

View 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) }
}
}

View 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
View 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
View 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
View 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();

View 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
View 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!();
}

View 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));
}
}
}
}

View 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
View file

@ -0,0 +1,4 @@
use super::*;
pub fn init(drive: Pci) {
}

126
kernel-rs/src/pci/mod.rs Normal file
View 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 }
}

View 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()
}
}

View 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");
}

View 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!();
}
}

View 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
View 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);
}

View 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))
}
}

View 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
View 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

@ -0,0 +1 @@
Subproject commit 229c4acbbe81bf4837bd05714b99614ada9dd2b3

View 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"
}