springboard to rust; VGA text mode driver and basics of TTY system

This commit is contained in:
c0repwn3r 2023-01-29 21:15:27 -05:00
commit b5aea81535
Signed by: core
GPG Key ID: FDBF740DADDCEECF
21 changed files with 589 additions and 0 deletions

6
.cargo/config.toml Normal file
View File

@ -0,0 +1,6 @@
[build]
target = "x86_64-unknown-shadeos-gnu.json"
[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/Cargo.lock

8
.idea/.gitignore vendored Normal file
View File

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

21
.idea/misc.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MakefileSettings">
<option name="linkedExternalProjectsSettings">
<MakefileProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
<option name="version" value="2" />
</MakefileProjectSettings>
</option>
</component>
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
</project>

8
.idea/rustyshade.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="Python" name="Python facet">
<configuration sdkName="Python 3.10" />
</facet>
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

17
Cargo.toml Normal file
View File

@ -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"

45
Makefile Normal file
View File

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

7
iso/boot/grub/grub.cfg Normal file
View File

@ -0,0 +1,7 @@
set timeout=0
set default=0
menuentry "shadeOS" {
multiboot2 /boot/shade.bin
boot
}

15
linker.ld Normal file
View File

@ -0,0 +1,15 @@
ENTRY(_start)
SECTIONS {
. = 1M;
.boot :
{
*(.mb2_header)
}
.text :
{
*(.text)
}
}

1
src/drivers/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod tty;

1
src/drivers/tty/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod vga;

28
src/drivers/tty/vga.rs Normal file
View File

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

67
src/lib.rs Normal file
View File

@ -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{}
}

144
src/llb/entry32.asm Normal file
View File

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

14
src/llb/mb2_header.asm Normal file
View File

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

121
src/tty.rs Normal file
View File

@ -0,0 +1,121 @@
use core::error::Error;
use core::fmt::{Display, Formatter};
pub struct TTY<const C: usize> {
pub ttys: [VirtualTTY; C],
pub selected: usize
}
impl<const C: usize> TTY<C> {
pub fn new() -> TTY<C> {
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<ROWS_IN_TTY, COLS>,
pub screen_buffer: VirtualTTYBuffer<ROWS_ON_SCREEN, COLS>,
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<const R: usize, const C: usize> = [[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>;
}

2
src/unsafe_utils/mod.rs Normal file
View File

@ -0,0 +1,2 @@
/// Unsafe utility functions
pub mod writes;

View File

@ -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;
}

56
tools/mkasmsg.py Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/python3
import sys
def help():
print('Usage: mkasmmsg.py -b<32/64> <format> <message>')
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)))

View File

@ -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
}