commit b5aea815356483296a4e915675f80670a193f837 Author: c0repwn3r Date: Sun Jan 29 21:15:27 2023 -0500 springboard to rust; VGA text mode driver and basics of TTY system diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..b611c93 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target = "x86_64-unknown-shadeos-gnu.json" + +[unstable] +build-std = ["core", "compiler_builtins"] +build-std-features = ["compiler-builtins-mem"] \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..760c1e8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/rustyshade.iml b/.idea/rustyshade.iml new file mode 100644 index 0000000..03036ae --- /dev/null +++ b/.idea/rustyshade.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..924c98c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "shadeos" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["staticlib"] + +[dependencies] +lazy_static = {version = "1.4.0", features = ["spin_no_std"]} + +[profile.dev] +panic = "abort" +[profile.release] +panic = "abort" \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..98f0678 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +ASM := nasm +ASMARGS := -f elf64 + +LD := x86_64-elf-ld +LDARGS := --nmagic + +CARGO_ENV := debug +CARGO_EXTRA_ARG := #--release + +.PHONY = target clean run rustc build debug +default: build + +clean: + cargo clean + +build: target/shade.iso + +rustc: + RUST_TARGET_PATH=$(shell pwd) cargo build $(CARGO_EXTRA_ARG) + +run: target/shade.iso + qemu-system-x86_64 -cdrom target/shade.iso +debug: target/shade.iso + qemu-system-x86_64 -cdrom target/shade.iso -s -S + +target/shade.iso: target/shade.bin iso/boot/grub/grub.cfg + cp -r iso target + cp target/shade.bin target/iso/boot/shade.bin + cd target + cd target && grub-mkrescue -o shade.iso iso + + +target/shade.bin: target/llb/entry32.o target/llb/mb2_header.o rustc + $(LD) $(LDARGS) --output=target/shade.bin --script=linker.ld target/llb/entry32.o target/llb/mb2_header.o target/x86_64-unknown-shadeos-gnu/$(CARGO_ENV)/libshadeos.a + +target/llb.a: target/llb/mb2_header.o target/llb/entry32.o + mkdir -p target/llb + ar rvs target/llb.a target/llb/mb2_header.o target/llb/entry32.o + +target/llb/mb2_header.o: src/llb/mb2_header.asm + mkdir -p target/llb + $(ASM) $(ASMARGS) src/llb/mb2_header.asm -o target/llb/mb2_header.o +target/llb/entry32.o: src/llb/entry32.asm + mkdir -p target/llb + $(ASM) $(ASMARGS) src/llb/entry32.asm -o target/llb/entry32.o \ No newline at end of file diff --git a/iso/boot/grub/grub.cfg b/iso/boot/grub/grub.cfg new file mode 100644 index 0000000..2e112d4 --- /dev/null +++ b/iso/boot/grub/grub.cfg @@ -0,0 +1,7 @@ +set timeout=0 +set default=0 + +menuentry "shadeOS" { + multiboot2 /boot/shade.bin + boot +} \ No newline at end of file diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..d044eda --- /dev/null +++ b/linker.ld @@ -0,0 +1,15 @@ +ENTRY(_start) + +SECTIONS { + . = 1M; + + .boot : + { + *(.mb2_header) + } + + .text : + { + *(.text) + } +} \ No newline at end of file diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs new file mode 100644 index 0000000..4a77f74 --- /dev/null +++ b/src/drivers/mod.rs @@ -0,0 +1 @@ +pub mod tty; \ No newline at end of file diff --git a/src/drivers/tty/mod.rs b/src/drivers/tty/mod.rs new file mode 100644 index 0000000..9b00c35 --- /dev/null +++ b/src/drivers/tty/mod.rs @@ -0,0 +1 @@ +pub mod vga; \ No newline at end of file diff --git a/src/drivers/tty/vga.rs b/src/drivers/tty/vga.rs new file mode 100644 index 0000000..3330f50 --- /dev/null +++ b/src/drivers/tty/vga.rs @@ -0,0 +1,28 @@ +use crate::tty::{TTYCharacter, TTYDriver, TTYDriverError}; +use crate::unsafe_utils::writes::write_dword; + +/// Implementation of TTYDriver for 0xb8000 VGA Text Mode + +pub struct VGATTYDriver {} +impl TTYDriver for VGATTYDriver { + fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError> { + if row > 25 { + return Err(TTYDriverError::RowOutOfBounds { got: row }) + } + if col > 80 { + return Err(TTYDriverError::ColOutOfBounds { got: col }) + } + // calculate the memory offset + let write_location = 0xb8000 + (row * 80 + col) * 2; + + // convert the TTYCharacter into a raw u16 to be written to memory + let raw_char = (char.format as u16) << 8 | char.char as u16; + + // and then write it using unsafe_utils + unsafe { + write_dword(raw_char, write_location); + } + + Ok(()) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1c847cd --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,67 @@ +#![no_std] +#![feature(error_in_core)] +#![feature(panic_info_message)] + +pub mod tty; +pub mod drivers; +pub mod unsafe_utils; + + +use core::panic::PanicInfo; +use crate::drivers::tty::vga::VGATTYDriver; +use crate::tty::{Color, TTY, TTYCharacter, TTYDriver, TTYDriverError}; + +/// This is our kpanic function. This will eventually use any functioning video driver to dump the kernel, but for now it just hangs +#[panic_handler] +fn kpanic(_info: &PanicInfo) -> ! { + let panic_str = _info.location().unwrap(); + loop {} +} + +#[no_mangle] +pub extern fn kmain() -> ! { + // initialize ttys and the kernel buffer + // just one for now + let vga_driver = VGATTYDriver {}; + let mut ttys: TTY<2> = TTY::new(); + ttys.select(0); + ttys.ttys[ttys.selected].screen_buffer[0][0] = TTYCharacter::new('s', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][1] = TTYCharacter::new('h', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][2] = TTYCharacter::new('a', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][3] = TTYCharacter::new('d', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][4] = TTYCharacter::new('e', Color::White, Color::Black); + + ttys.select(1); + ttys.ttys[ttys.selected].screen_buffer[0][0] = TTYCharacter::new('o', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][1] = TTYCharacter::new('s', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][2] = TTYCharacter::new(' ', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][3] = TTYCharacter::new(':', Color::White, Color::Black); + ttys.ttys[ttys.selected].screen_buffer[0][4] = TTYCharacter::new(')', Color::White, Color::Black); + + match ttys.select_and_update(0, &vga_driver) { + Ok(_) => {} + Err(e) => { + match e { + TTYDriverError::ColOutOfBounds { got } => { + vga_driver.set_char(0, 5, TTYCharacter::new('y', Color::Red, Color::Black)).unwrap(); + loop {} + }, + TTYDriverError::RowOutOfBounds { got } => { + vga_driver.set_char(0, 5, TTYCharacter::new('y', Color::Red, Color::Black)).unwrap(); + loop {} + } + } + } + } + + for i in 0..2000000 { + continue; + } + + ttys.select_and_update(1, &vga_driver).unwrap(); + + //vga_driver.set_char(0, 0, TTYCharacter::new('s', Color::White, Color::Black)).unwrap(); + //vga_driver.set_char(0, 1, TTYCharacter::new('h', Color::White, Color::Black)).unwrap(); + + loop{} +} \ No newline at end of file diff --git a/src/llb/entry32.asm b/src/llb/entry32.asm new file mode 100644 index 0000000..9fa3063 --- /dev/null +++ b/src/llb/entry32.asm @@ -0,0 +1,144 @@ +global _start +extern kmain + +section .text +bits 32 + +; LLB MESSAGE CODES: +; llb:01 - llb execution started +; llb:02 - llb got execution back, system panic +; llb:03 - configuring page tables +; llb:04 - page tables configured with no issues +; llb:05 - gdt loaded, handoff to rbt +; llb:6 - handoff to long mode successful + +_start: + ; llb:01 + mov word [0xb8000], 0x026c + mov word [0xb8002], 0x026c + mov word [0xb8004], 0x0262 + mov word [0xb8006], 0x023a + mov word [0xb8008], 0x0230 + mov word [0xb800a], 0x0231 + + ; llb:03 + mov word [0xb8010], 0x026c + mov word [0xb8012], 0x026c + mov word [0xb8014], 0x0262 + mov word [0xb8016], 0x023a + mov word [0xb8018], 0x0230 + mov word [0xb801a], 0x0233 + + ; Point the first entry of the level 4 page table to the first entry in the + ; p3 table + mov eax, p3_table + or eax, 0b11 + mov dword [p4_table + 0], eax + + mov eax, p2_table + or eax, 0b11 + mov dword [p3_table + 0], eax + + mov ecx, 0 ; loop counter +.map_p2_table: + mov eax, 0x200000 ; 2 MiB huge pages + mul ecx + or eax, 0b10000011 ; huge pages + mov [p2_table + ecx * 8], eax ; mark in the page table + inc ecx + cmp ecx, 512 ; 512 2 MiB pages = 1024 MiB paged memory; 1 GiB usable memory + jne .map_p2_table + + ; put address of page table in cr3 + mov eax, p4_table + mov cr3, eax + + ; enable pae + mov eax, cr4 + or eax, 1 << 5 + mov cr4, eax + + ; set long mode bit + mov ecx, 0xc0000080 + rdmsr + or eax, 1 << 8 + wrmsr + + ; enable paging + mov eax, cr0 + or eax, 1 << 31 + or eax, 1 << 16 + mov cr0, eax + + ; llb:04 + mov word [0xb8020], 0x026c + mov word [0xb8022], 0x026c + mov word [0xb8024], 0x0262 + mov word [0xb8026], 0x023a + mov word [0xb8028], 0x0230 + mov word [0xb802a], 0x0234 + + lgdt [gdt64.pointer] + + ; update selectors + mov ax, gdt64.data + mov ss, ax + mov ds, ax + mov es, ax + + ; llb:05 + mov word [0xb8030], 0x026c + mov word [0xb8032], 0x026c + mov word [0xb8034], 0x0262 + mov word [0xb8036], 0x023a + mov word [0xb8038], 0x0230 + mov word [0xb803a], 0x0235 + + jmp gdt64.code:rbt_entry + + ; llb:02 + mov word [0xb8200], 0x026c + mov word [0xb8202], 0x026c + mov word [0xb8204], 0x0262 + mov word [0xb8206], 0x023a + mov word [0xb8208], 0x0230 + mov word [0xb820a], 0x0232 + hlt + +section .bss +align 4096 + +p4_table: + resb 4096 +p3_table: + resb 4096 +p2_table: + resb 4096 + +section .rodata +section .rodata +gdt64: + dq 0 +.code: equ $ - gdt64 + dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) +.data: equ $ - gdt64 + dq (1<<44) | (1<<47) | (1<<41) +.pointer: + dw .pointer - gdt64 - 1 + dq gdt64 + +section .text +bits 64 +rbt_entry: + + ; llb:06 + mov word [0xb8040], 0x026c + mov word [0xb8042], 0x026c + mov word [0xb8044], 0x0262 + mov word [0xb8046], 0x023a + mov word [0xb8048], 0x0230 + mov word [0xb804a], 0x0236 + + jmp kmain + + hlt \ No newline at end of file diff --git a/src/llb/mb2_header.asm b/src/llb/mb2_header.asm new file mode 100644 index 0000000..62045a5 --- /dev/null +++ b/src/llb/mb2_header.asm @@ -0,0 +1,14 @@ +section .mb2_header +header_start: + dd 0xe85250d6 ; magic number + dd 0 ; protected mode code + dd header_end - header_start ; header length + + ; checksum + dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) + + ; required end tag + dw 0 ; type + dw 0 ; flags + dd 8 ; size +header_end: \ No newline at end of file diff --git a/src/tty.rs b/src/tty.rs new file mode 100644 index 0000000..435478a --- /dev/null +++ b/src/tty.rs @@ -0,0 +1,121 @@ +use core::error::Error; +use core::fmt::{Display, Formatter}; + +pub struct TTY { + pub ttys: [VirtualTTY; C], + pub selected: usize +} +impl TTY { + pub fn new() -> TTY { + Self { + ttys: [ + VirtualTTY::new(); C + ], + selected: 0 + } + } + + pub fn select(&mut self, new: usize) { self.selected = new; } + + pub fn update(&self, driver: &impl TTYDriver) -> Result<(), TTYDriverError> { + let selected_tty = &self.ttys[self.selected]; + for (row_num, row) in selected_tty.screen_buffer.iter().enumerate() { + for (col_num, col) in row.iter().enumerate() { + driver.set_char(row_num, col_num, *col)?; + } + } + Ok(()) + } + + pub fn select_and_update(&mut self, new: usize, driver: &impl TTYDriver) -> Result<(), TTYDriverError> { + self.select(new); + self.update(driver) + } +} + + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[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(Copy, Clone)] +pub struct TTYCharacter { + pub format: u8, + pub char: char +} + +impl TTYCharacter { + pub fn blank() -> Self { Self { format: 0u8, char: ' ' } } + pub fn from(char: char) -> Self { Self { format: 0x0fu8, char }} + pub fn new(char: char, fg: Color, bg: Color) -> Self { + Self { + format: (fg as u8) << 4 | bg as u8, + char + } + } +} + +pub const ROWS_ON_SCREEN: usize = 25; +pub const COLS: usize = 80; + +pub const ROWS_IN_TTY: usize = 80; + +#[derive(Copy, Clone)] +pub struct VirtualTTY { + pub full_buffer: VirtualTTYBuffer, + pub screen_buffer: VirtualTTYBuffer, + pub line_offset: usize +} +impl Default for VirtualTTY { + fn default() -> Self { + Self { + full_buffer: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY], + screen_buffer: [[TTYCharacter::blank(); COLS]; ROWS_ON_SCREEN], + line_offset: ROWS_IN_TTY - ROWS_ON_SCREEN, // assume we are scrolled all the way down + } + } +} +impl VirtualTTY { + pub fn new() -> Self { + Self::default() + } +} + +type VirtualTTYBuffer = [[TTYCharacter; C]; R]; + +#[derive(Debug)] +pub enum TTYDriverError { + RowOutOfBounds { got: usize }, + ColOutOfBounds { got: usize } +} +impl Display for TTYDriverError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::RowOutOfBounds { got } => write!(f, "Character write out of bounds: row {} too high", got), + Self::ColOutOfBounds { got } => write!(f, "Character write out of bounds: col {} too high", got) + } + } +} +impl Error for TTYDriverError {} + +pub trait TTYDriver { + fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError>; +} \ No newline at end of file diff --git a/src/unsafe_utils/mod.rs b/src/unsafe_utils/mod.rs new file mode 100644 index 0000000..740e959 --- /dev/null +++ b/src/unsafe_utils/mod.rs @@ -0,0 +1,2 @@ +/// Unsafe utility functions +pub mod writes; \ No newline at end of file diff --git a/src/unsafe_utils/writes.rs b/src/unsafe_utils/writes.rs new file mode 100644 index 0000000..1312efd --- /dev/null +++ b/src/unsafe_utils/writes.rs @@ -0,0 +1,5 @@ +/// Write the given dword to any arbitrary memory location. This is an inherently unsafe operation; hence the function is unsafe +pub unsafe fn write_dword(dword: u16, at: usize) { + let ptr = at as *mut u16; + *ptr = dword; +} \ No newline at end of file diff --git a/tools/mkasmsg.py b/tools/mkasmsg.py new file mode 100755 index 0000000..c26209a --- /dev/null +++ b/tools/mkasmsg.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +import sys + + +def help(): + print('Usage: mkasmmsg.py -b<32/64> ') + + +if len(sys.argv) != 4: + help() + sys.exit(1) + +format = sys.argv[2] +msg = sys.argv[3] +base = 0xb8000 + +index = { + '1': 'word', + '2': 'dword', + '4': 'qword' +} + +command = 'mov {} [{}], 0x{}' + +if sys.argv[1] == '-b32': + groupsize = 2 +elif sys.argv[1] == '-b64': + groupsize = 4 +else: + help() + sys.exit(1) + + +def chunk(iterator, n): + for i in range(0, len(iterator), n): + yield iterator[i:i + n] + + +raw_nums = [] + +for char in list(msg): + raw_nums.append(format + hex(ord(char))[2:]) + +total = 0 + +for num, block in enumerate(list(chunk(raw_nums, groupsize))): + if len(block) == 3: + total += 2 + print(command.format('word', hex(base + total), block[0])) + total += 4 + print(command.format('dword', hex(base + total), ''.join(block[1:]))) + continue + total += len(block) * groupsize + print(command.format(index[str(len(block))], hex(base + total), ''.join(block))) + + diff --git a/x86_64-unknown-shadeos-gnu.json b/x86_64-unknown-shadeos-gnu.json new file mode 100644 index 0000000..f2487c9 --- /dev/null +++ b/x86_64-unknown-shadeos-gnu.json @@ -0,0 +1,15 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "llvm-target": "x86_64-unknown-none-gnu", + "linker-flavor": "gcc", + "no-compiler-rt": true, + "os": "intermezzos", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "features": "-mmx,-fxsr,-sse,-sse2,+soft-float", + "disable-redzone": true, + "eliminate-frame-pointer": false +} \ No newline at end of file