feature: framebuffer console WIP

This commit is contained in:
Mark Poliakov 2021-12-22 23:56:55 +02:00
parent ec5b5bc31b
commit d4323e3c8f
26 changed files with 766 additions and 98 deletions

9
Cargo.lock generated
View File

@ -97,6 +97,7 @@ dependencies = [
"kernel-macros", "kernel-macros",
"libsys", "libsys",
"memfs", "memfs",
"multiboot2",
"tock-registers", "tock-registers",
"vfs", "vfs",
] ]
@ -162,6 +163,14 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "multiboot2"
version = "0.12.2"
source = "git+https://github.com/alnyan/multiboot2?branch=expose-extra-traits-for-iters#7e86b55fa5ab82e54978021f8022068a1591166b"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.3.3" version = "0.3.3"

View File

@ -31,7 +31,7 @@ QEMU_OPTS=-s
ifeq ($(ARCH),x86_64) ifeq ($(ARCH),x86_64)
MACH=none MACH=none
QEMU_OPTS+=-cdrom $(O)/image.iso \ QEMU_OPTS+=-cdrom $(O)/image.iso \
-M q35,accel=kvm \ -M q35 \
-cpu host \ -cpu host \
-enable-kvm \ -enable-kvm \
-m 512 \ -m 512 \

BIN
etc/default8x16.psfu Normal file

Binary file not shown.

View File

@ -22,5 +22,8 @@ SECTIONS {
.bss : AT(. - KERNEL_OFFSET) { .bss : AT(. - KERNEL_OFFSET) {
*(COMMON) *(COMMON)
*(.bss*) *(.bss*)
. = ALIGN(4K);
} }
PROVIDE(__kernel_end = .);
} }

View File

@ -19,6 +19,9 @@ bitflags = "^1.3.0"
kernel-macros = { path = "macros" } kernel-macros = { path = "macros" }
fs-macros = { path = "../fs/macros" } fs-macros = { path = "../fs/macros" }
[target.'cfg(target_arch = "x86_64")'.dependencies]
multiboot2 = { git = "https://github.com/alnyan/multiboot2", branch = "expose-extra-traits-for-iters" }
[target.'cfg(target_arch = "aarch64")'.dependencies] [target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "6.x.x" } cortex-a = { version = "6.x.x" }
fdt-rs = { version = "0.x.x", default-features = false } fdt-rs = { version = "0.x.x", default-features = false }

View File

@ -8,6 +8,14 @@
.long ARCH .long ARCH
.long HDRLEN .long HDRLEN
.long CHKSUM .long CHKSUM
.short 5
.short 0
.long 20
.long 800
.long 600
.long 32
.short 0 .short 0
.long 8 .long 8

View File

@ -1,15 +1,105 @@
use crate::arch::x86_64::{gdt, idt}; use crate::arch::x86_64::{self, intc, gdt, idt};
use crate::mem::{
self, heap,
phys::{self, MemoryRegion, PageUsage, ReservedRegion},
virt,
};
use crate::debug;
use crate::fs::{devfs, sysfs};
use crate::dev::{pseudo, Device, display::FramebufferInfo};
use core::mem::MaybeUninit;
use crate::font;
use multiboot2::{BootInformation, MemoryArea};
static mut RESERVED_REGION_MB2: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
#[no_mangle] #[no_mangle]
extern "C" fn __x86_64_bsp_main(mb_checksum: u32, mb_info_ptr: u32) -> ! { extern "C" fn __x86_64_bsp_main(mb_checksum: u32, mb_info_ptr: u32) -> ! {
// TODO enable FP support for kernel/user
// Setup a proper GDT
unsafe { unsafe {
// Enable SSE support
asm!(
r#"
mov %cr4, %rax
or $(1 << 9), %rax // FXSAVE, FXRSTOR
or $(1 << 10), %rax // OSXMMEXCPT
mov %rax, %cr4
mov %cr0, %rax
and $~(1 << 2), %rax // Disable EM
or $(1 << 1), %rax // Enable MP
mov %rax, %cr0
"#,
options(att_syntax)
);
// Setup a proper GDT
gdt::init(); gdt::init();
idt::init(|_| {}); idt::init(intc::map_isr_entries);
} }
loop {} virt::enable().expect("Failed to initialize virtual memory");
let mb_info = unsafe {
multiboot2::load_with_offset(mb_info_ptr as usize, mem::KERNEL_OFFSET)
.expect("Failed to load multiboot info structure")
};
unsafe {
let mb_info_page = (mb_info_ptr & !0xFFF) as usize;
RESERVED_REGION_MB2.write(ReservedRegion::new(
mb_info_page,
mb_info_page + ((mb_info.total_size() + 0xFFF) & !0xFFF),
));
phys::reserve("multiboot2", RESERVED_REGION_MB2.as_mut_ptr());
phys::init_from_iter(
mb_info
.memory_map_tag()
.unwrap()
.memory_areas()
.map(|entry| MemoryRegion {
start: ((entry.start_address() + 0xFFF) & !0xFFF) as usize,
end: (entry.end_address() & !0xFFF) as usize,
}),
);
}
// Setup a heap
unsafe {
let heap_base_phys = phys::alloc_contiguous_pages(PageUsage::KernelHeap, 4096)
.expect("Failed to allocate memory for heap");
let heap_base_virt = mem::virtualize(heap_base_phys);
heap::init(heap_base_virt, 16 * 1024 * 1024);
}
// Setup hardware
unsafe {
x86_64::INTC.enable();
}
let fb_info = mb_info.framebuffer_tag().unwrap();
let virt = mem::virtualize(fb_info.address as usize);
debugln!("Framebuffer base: phys={:#x}, virt={:#x}", fb_info.address, virt);
x86_64::DISPLAY.set_framebuffer(FramebufferInfo {
width: fb_info.width as usize,
height: fb_info.height as usize,
phys_base: fb_info.address as usize,
virt_base: virt
});
font::init();
debug::set_display(&x86_64::DISPLAY);
devfs::init();
sysfs::init();
devfs::add_named_char_device(&pseudo::ZERO, "zero").unwrap();
devfs::add_named_char_device(&pseudo::RANDOM, "random").unwrap();
loop {
unsafe {
asm!("sti; hlt");
}
}
} }
global_asm!(include_str!("macros.S"), options(att_syntax)); global_asm!(include_str!("macros.S"), options(att_syntax));

View File

@ -2,57 +2,48 @@
.section .text._entry .section .text._entry
__x86_64_enter_upper: __x86_64_enter_upper:
// Setup paging table mov $(PTE_PRESENT | PTE_WRITABLE | PTE_USERSPACE), %edx
mov $(PTE_PRESENT | PTE_BLOCK | PTE_WRITABLE | PTE_USERSPACE), %eax // Setup PML4
// Fill PD0: 0..1GiB lea (KERNEL_FIXED - KERNEL_OFFSET), %edi
lea (KERNEL_PD0 - KERNEL_OFFSET), %edi lea (KERNEL_FIXED + 4096 - KERNEL_OFFSET), %esi
mov $512, %ecx mov %edx, %eax
1: or %esi, %eax
dec %ecx // pml4[0] = %eax
mov %eax, (%edi)
mov %ecx, %edx // pml4[511] = %eax
shl $21, %edx mov %eax, 4088(%edi)
or %eax, %edx
mov %edx, (%edi, %ecx, 8)
test %ecx, %ecx
jnz 1b
// Fill PD1: 1GiB..2GiB
lea (KERNEL_PD1 - KERNEL_OFFSET), %edi
mov $512, %ecx
1:
dec %ecx
mov %ecx, %edx
add $512, %edx
shl $21, %edx
or %eax, %edx
mov %edx, (%edi, %ecx, 8)
test %ecx, %ecx
jnz 1b
// Setup PDPT // Setup PDPT
mov $(PTE_PRESENT | PTE_WRITABLE | PTE_USERSPACE), %eax mov %esi, %edi
lea (KERNEL_PDPT - KERNEL_OFFSET), %edi lea (KERNEL_FIXED + 8192 - KERNEL_OFFSET), %esi
xor %ecx, %ecx
1:
// %eax = &table[%ecx] | attrs
mov %esi, %eax
or %edx, %eax
mov %eax, (%edi, %ecx, 8)
lea (KERNEL_PD0 - KERNEL_OFFSET), %esi add $4096, %esi
or %eax, %esi inc %ecx
mov %esi, (%edi) cmp $16, %ecx
jne 1b
lea (KERNEL_PD1 - KERNEL_OFFSET), %esi // Setup PDs
or %eax, %esi lea (KERNEL_FIXED + 8192 - KERNEL_OFFSET), %edi
mov %esi, 8(%edi) mov $(PTE_PRESENT | PTE_BLOCK | PTE_WRITABLE), %edx
mov $(512 * 16), %ecx
1:
dec %ecx
// Setup PML4 // %eax = attrs | (i << 21)
lea (KERNEL_PML4 - KERNEL_OFFSET), %edi mov %ecx, %eax
lea (KERNEL_PDPT - KERNEL_OFFSET), %esi shl $21, %eax
or %eax, %esi or %edx, %eax
mov %esi, (%edi)
mov %esi, 4088(%edi) mov %eax, (%edi, %ecx, 8)
test %ecx, %ecx
jnz 1b
// Enable PAE/PSE // Enable PAE/PSE
mov %cr4, %eax mov %cr4, %eax
@ -66,6 +57,7 @@ __x86_64_enter_upper:
wrmsr wrmsr
// Set CR3 // Set CR3
lea (KERNEL_FIXED - KERNEL_OFFSET), %edi
mov %edi, %cr3 mov %edi, %cr3
// Enable paging // Enable paging

View File

View File

@ -1,4 +1,6 @@
use crate::arch::x86_64;
use crate::debug::Level; use crate::debug::Level;
use crate::dev::irq::{IrqContext, IntController};
#[derive(Debug)] #[derive(Debug)]
struct ExceptionFrame { struct ExceptionFrame {
@ -68,3 +70,11 @@ extern "C" fn __x86_64_exception_handler(frame: &mut ExceptionFrame) {
panic!("Unhandled exception"); panic!("Unhandled exception");
} }
#[no_mangle]
extern "C" fn __x86_64_irq_handler(frame: &mut ExceptionFrame) {
unsafe {
let ic = IrqContext::new(frame.err_no as usize);
x86_64::intc().handle_pending_irqs(&ic);
}
}

View File

@ -18,11 +18,11 @@ struct Pointer {
offset: usize offset: usize
} }
const SIZE: usize = 256; pub const SIZE: usize = 256;
impl Entry { impl Entry {
const PRESENT: u8 = 1 << 7; pub const PRESENT: u8 = 1 << 7;
const INT32: u8 = 0xE; pub const INT32: u8 = 0xE;
pub const fn new(base: usize, selector: u16, flags: u8) -> Self { pub const fn new(base: usize, selector: u16, flags: u8) -> Self {
Self { Self {
@ -60,6 +60,8 @@ pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE]) -> ()>(f: F) {
IDT[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32); IDT[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32);
} }
f(&mut IDT);
let idtr = Pointer { let idtr = Pointer {
limit: size_of_val(&IDT) as u16 - 1, limit: size_of_val(&IDT) as u16 - 1,
offset: &IDT as *const _ as usize offset: &IDT as *const _ as usize

View File

@ -0,0 +1,144 @@
use crate::arch::x86_64::{
idt::{Entry as IdtEntry, SIZE as IDT_SIZE},
PortIo,
};
use crate::dev::{
irq::{IntController, IntSource, IrqContext},
Device,
};
use crate::sync::IrqSafeSpinLock;
use libsys::error::Errno;
const ICW1_INIT: u8 = 0x10;
const ICW1_ICW4: u8 = 0x01;
const ICW4_8086: u8 = 0x01;
pub(super) struct I8259 {
cmd_a: PortIo<u8>,
cmd_b: PortIo<u8>,
data_a: PortIo<u8>,
data_b: PortIo<u8>,
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; 15]>,
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct IrqNumber(u32);
impl IrqNumber {
pub const MAX: u32 = 16;
pub const fn new(u: u32) -> Self {
if u > Self::MAX {
panic!();
}
Self(u)
}
}
impl Device for I8259 {
fn name(&self) -> &'static str {
"i8259-compatible IRQ controller"
}
unsafe fn enable(&self) -> Result<(), Errno> {
self.cmd_a.write(ICW1_INIT | ICW1_ICW4);
self.cmd_b.write(ICW1_INIT | ICW1_ICW4);
self.data_a.write(32);
self.data_b.write(32 + 8);
self.data_a.write(4);
self.data_b.write(2);
self.data_a.write(ICW4_8086);
self.data_b.write(ICW4_8086);
self.data_a.write(0xFE);
self.data_b.write(0xFF);
Ok(())
}
}
impl IntController for I8259 {
type IrqNumber = IrqNumber;
fn register_handler(
&self,
irq: Self::IrqNumber,
handler: &'static (dyn IntSource + Sync),
) -> Result<(), Errno> {
if irq.0 == 0 {
return Err(Errno::InvalidArgument);
}
let index = (irq.0 - 1) as usize;
let mut lock = self.table.lock();
if lock[index].is_some() {
return Err(Errno::AlreadyExists);
}
lock[index] = Some(handler);
Ok(())
}
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno> {
let port = if irq.0 < 8 {
&self.data_a
} else {
&self.data_b
};
let mask = port.read() & !(1 << (irq.0 & 0x7));
port.write(mask);
Ok(())
}
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
let irq_number = ic.token();
assert!(irq_number > 0);
if irq_number > 8 {
self.cmd_b.write(0x20);
}
self.cmd_a.write(0x20);
{
let table = self.table.lock();
match table[irq_number - 1] {
None => panic!("No handler registered for irq{}", irq_number),
Some(handler) => {
drop(table);
handler.handle_irq().expect("irq handler failed")
}
}
}
}
}
impl I8259 {
pub const fn new() -> Self {
unsafe {
Self {
cmd_a: PortIo::new(0x20),
data_a: PortIo::new(0x21),
cmd_b: PortIo::new(0xA0),
data_b: PortIo::new(0xA1),
table: IrqSafeSpinLock::new([None; 15]),
}
}
}
}
pub fn map_isr_entries(entries: &mut [IdtEntry; IDT_SIZE]) {
extern "C" {
static __x86_64_irq_vectors: [usize; 16];
}
for (i, &entry) in unsafe { __x86_64_irq_vectors.iter().enumerate() } {
entries[i + 32] = IdtEntry::new(entry, 0x08, IdtEntry::PRESENT | IdtEntry::INT32);
}
}
global_asm!(include_str!("irq_vectors.S"), options(att_syntax));

View File

@ -0,0 +1,83 @@
.macro irq_entry no
__x86_64_irq_\no:
cli
pushq $0
pushq $\no
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
mov %rsp, %rdi
call __x86_64_irq_handler
jmp .
.endm
.section .text
__x86_64_irq_0:
cli
push %rax
push %rdx
mov $'T', %al
mov $0x3F8, %dx
outb %al, %dx
mov $0x20, %al
mov $0x20, %dx
outb %al, %dx
pop %rdx
pop %rax
iretq
irq_entry 1
irq_entry 2
irq_entry 3
irq_entry 4
irq_entry 5
irq_entry 6
irq_entry 7
irq_entry 8
irq_entry 9
irq_entry 10
irq_entry 11
irq_entry 12
irq_entry 13
irq_entry 14
irq_entry 15
.section .rodata
.global __x86_64_irq_vectors
__x86_64_irq_vectors:
.quad __x86_64_irq_0
.quad __x86_64_irq_1
.quad __x86_64_irq_2
.quad __x86_64_irq_3
.quad __x86_64_irq_4
.quad __x86_64_irq_5
.quad __x86_64_irq_6
.quad __x86_64_irq_7
.quad __x86_64_irq_8
.quad __x86_64_irq_9
.quad __x86_64_irq_10
.quad __x86_64_irq_11
.quad __x86_64_irq_12
.quad __x86_64_irq_13
.quad __x86_64_irq_14
.quad __x86_64_irq_15

View File

@ -1,7 +1,10 @@
use crate::dev::serial::SerialDevice; use crate::dev::{serial::SerialDevice, display::StaticFramebuffer, irq::IntController};
mod uart; mod uart;
use uart::Uart; use uart::Uart;
mod intc;
use intc::I8259;
mod io; mod io;
pub(self) use io::PortIo; pub(self) use io::PortIo;
@ -18,7 +21,9 @@ pub(self) mod exception;
/// Unsafe: disables IRQ handling temporarily /// Unsafe: disables IRQ handling temporarily
#[inline(always)] #[inline(always)]
pub unsafe fn irq_mask_save() -> u64 { pub unsafe fn irq_mask_save() -> u64 {
loop {} let mut res;
asm!("pushf; cli; pop {}", out(reg) res, options(att_syntax));
res
} }
/// Restores IRQ mask state /// Restores IRQ mask state
@ -29,7 +34,13 @@ pub unsafe fn irq_mask_save() -> u64 {
/// conjunction with [irq_mask_save] /// conjunction with [irq_mask_save]
#[inline(always)] #[inline(always)]
pub unsafe fn irq_restore(state: u64) { pub unsafe fn irq_restore(state: u64) {
loop {} if state & (1 << 9) != 0 {
asm!("sti");
}
}
pub fn intc() -> &'static impl IntController {
&INTC
} }
pub fn console() -> &'static impl SerialDevice { pub fn console() -> &'static impl SerialDevice {
@ -37,3 +48,5 @@ pub fn console() -> &'static impl SerialDevice {
} }
static COM1: Uart = unsafe { Uart::new(0x3F8) }; static COM1: Uart = unsafe { Uart::new(0x3F8) };
static INTC: I8259 = I8259::new();
pub(self) static DISPLAY: StaticFramebuffer = StaticFramebuffer::uninit();

View File

@ -1,13 +1,22 @@
use crate::mem::virt::AddressSpace; use crate::mem::virt::AddressSpace;
use core::ops::{Index, IndexMut};
use libsys::error::Errno; use libsys::error::Errno;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct Entry(u64); pub struct Entry(u64);
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))] #[repr(C, align(0x1000))]
pub struct Table { pub struct Table {
entries: [Entry; 512], entries: [Entry; 512]
}
#[repr(C, align(0x1000))]
pub struct FixedTableGroup {
pml4: Table,
pdpt: Table,
pd: [Table; 16]
} }
#[repr(transparent)] #[repr(transparent)]
@ -15,14 +24,11 @@ pub struct Space(Table);
// Upper mappings // Upper mappings
#[no_mangle] #[no_mangle]
static KERNEL_PDPT: Table = Table::empty(); static mut KERNEL_FIXED: FixedTableGroup = FixedTableGroup {
#[no_mangle] pml4: Table::empty(),
static KERNEL_PD0: Table = Table::empty(); pdpt: Table::empty(),
#[no_mangle] pd: [Table::empty(); 16]
static KERNEL_PD1: Table = Table::empty(); };
#[no_mangle]
static KERNEL_PML4: Space = Space::empty();
impl Entry { impl Entry {
const fn invalid() -> Self { const fn invalid() -> Self {
@ -61,6 +67,27 @@ impl AddressSpace for Space {
} }
} }
pub fn enable() -> Result<(), Errno> { impl Index<usize> for Table {
loop {} type Output = Entry;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl IndexMut<usize> for Table {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
pub fn enable() -> Result<(), Errno> {
unsafe {
// Remove the lower mapping
KERNEL_FIXED.pml4.entries[0] = Entry::invalid();
// Flush the TLB by reloading cr3
asm!("mov %cr3, %rax; mov %rax, %cr3", options(att_syntax));
}
Ok(())
} }

View File

@ -11,12 +11,84 @@
//! * [warnln!] //! * [warnln!]
//! * [errorln!] //! * [errorln!]
use crate::dev::serial::SerialDevice; use crate::dev::{
use libsys::{debug::TraceLevel, error::Errno}; display::{Display, FramebufferInfo},
serial::SerialDevice,
};
use crate::font;
use crate::sync::IrqSafeSpinLock;
use core::convert::TryFrom; use core::convert::TryFrom;
use core::fmt; use core::fmt;
use libsys::{debug::TraceLevel, error::Errno};
pub static LEVEL: Level = Level::Debug; pub static LEVEL: Level = Level::Debug;
static COLOR_MAP: [u32; 16] = [
0x000000,
0x0000AA,
0x00AA00,
0x00AAAA,
0xAA0000,
0xAA00AA,
0xAA5500,
0xAAAAAA,
0x555555,
0x5555FF,
0x55FF55,
0x55FFFF,
0xFF5555,
0xFF55FF,
0xFFFF55,
0xFFFFFF,
];
static ATTR_MAP: [usize; 10] = [
0, 4, 2, 6, 1, 5, 3, 7, 7, 7
];
static DISPLAY: IrqSafeSpinLock<FramebufferOutput> = IrqSafeSpinLock::new(FramebufferOutput {
display: None,
col: 0,
row: 0,
fg: 0xBBBBBB,
bg: 0x000000,
esc: EscapeState::None,
esc_argv: [0; 8],
esc_argc: 0
});
enum EscapeState {
None,
Esc,
Data
}
struct FramebufferOutput {
display: Option<&'static dyn Display>,
row: usize,
col: usize,
fg: u32,
bg: u32,
esc: EscapeState,
esc_argv: [usize; 8],
esc_argc: usize
}
impl fmt::Write for FramebufferOutput {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.display.is_none() {
return Ok(());
}
let fb = self.display.unwrap().framebuffer().unwrap();
for ch in s.chars() {
self.putc(&fb, ch);
}
Ok(())
}
}
pub fn set_display(disp: &'static dyn Display) {
DISPLAY.lock().display = Some(disp);
}
/// Kernel logging levels /// Kernel logging levels
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
@ -42,7 +114,7 @@ impl TryFrom<u32> for Level {
2 => Ok(Level::Info), 2 => Ok(Level::Info),
3 => Ok(Level::Warn), 3 => Ok(Level::Warn),
4 => Ok(Level::Error), 4 => Ok(Level::Error),
_ => Err(Errno::InvalidArgument) _ => Err(Errno::InvalidArgument),
} }
} }
} }
@ -137,6 +209,10 @@ pub fn _debug(level: Level, args: fmt::Arguments) {
use crate::arch::machine; use crate::arch::machine;
use fmt::Write; use fmt::Write;
if level > Level::Debug {
DISPLAY.lock().write_fmt(args).ok();
}
if level >= LEVEL { if level >= LEVEL {
SerialOutput { SerialOutput {
inner: machine::console(), inner: machine::console(),
@ -145,3 +221,89 @@ pub fn _debug(level: Level, args: fmt::Arguments) {
.ok(); .ok();
} }
} }
impl FramebufferOutput {
const CW: usize = 8;
const CH: usize = 12;
pub fn set_char(&mut self, fb: &FramebufferInfo, x: usize, y: usize, ch: char) {
if (x + 1) * Self::CW >= fb.width || (y + 1) * Self::CH >= fb.height {
return;
}
font::get().draw(fb, x * Self::CW, y * Self::CH, ch, self.fg, self.bg);
}
pub fn putc(&mut self, fb: &FramebufferInfo, ch: char) {
match self.esc {
EscapeState::None => {
match ch {
'\x1B' => {
self.esc = EscapeState::Esc;
self.esc_argv.fill(0);
self.esc_argc = 0;
}
' '..='\x7E' => {
self.set_char(fb, self.col, self.row, ch);
// Advance the cursor
self.col += 1;
if (self.col + 1) * Self::CW >= fb.width {
self.col = 0;
self.row += 1;
}
}
'\n' => {
self.col = 0;
self.row += 1;
}
_ => {}
}
if (self.row + 1) * Self::CH >= fb.height {
todo!()
}
}
EscapeState::Esc => {
match ch {
'[' => {
self.esc = EscapeState::Data;
}
_ => {
self.esc = EscapeState::None;
}
}
}
EscapeState::Data => {
match ch {
'0'..='9' => {
self.esc_argv[self.esc_argc] *= 10;
self.esc_argv[self.esc_argc] += (ch as u8 - b'0') as usize;
}
';' => {
self.esc_argc += 1;
}
_ => {
self.esc_argc += 1;
self.esc = EscapeState::None;
}
}
match ch {
'm' => {
for i in 0..self.esc_argc {
let item = self.esc_argv[i];
if item / 10 == 4 {
self.bg = COLOR_MAP[ATTR_MAP[(item % 10) as usize]];
}
if item / 10 == 3 {
self.fg = COLOR_MAP[ATTR_MAP[(item % 10) as usize]];
}
}
}
_ => {}
}
}
}
}
}

58
kernel/src/dev/display.rs Normal file
View File

@ -0,0 +1,58 @@
use crate::dev::Device;
use libsys::error::Errno;
use crate::util::InitOnce;
pub struct FramebufferInfo {
pub width: usize,
pub height: usize,
pub phys_base: usize,
pub virt_base: usize
}
pub trait Display: Device {
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno>;
fn framebuffer<'a>(&'a self) -> Result<&'a FramebufferInfo, Errno>;
}
pub struct DisplayMode {
width: u16,
height: u16,
}
pub struct StaticFramebuffer {
framebuffer: InitOnce<FramebufferInfo>
}
impl Device for StaticFramebuffer {
fn name(&self) -> &'static str {
"Generic framebuffer device"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl Display for StaticFramebuffer {
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno> {
Err(Errno::InvalidOperation)
}
fn framebuffer(&self) -> Result<&FramebufferInfo, Errno> {
if let Some(fb) = self.framebuffer.as_ref_option() {
Ok(fb)
} else {
Err(Errno::InvalidOperation)
}
}
}
impl StaticFramebuffer {
pub const fn uninit() -> Self {
Self { framebuffer: InitOnce::new() }
}
pub fn set_framebuffer(&self, framebuffer: FramebufferInfo) {
self.framebuffer.init(framebuffer);
}
}

View File

@ -5,6 +5,7 @@ use libsys::error::Errno;
/// Token to indicate the local core is running in IRQ context /// Token to indicate the local core is running in IRQ context
pub struct IrqContext<'irq_context> { pub struct IrqContext<'irq_context> {
token: usize,
_0: PhantomData<&'irq_context ()>, _0: PhantomData<&'irq_context ()>,
} }
@ -45,7 +46,11 @@ impl<'q> IrqContext<'q> {
/// ///
/// Only allowed to be constructed in top-level IRQ handlers /// Only allowed to be constructed in top-level IRQ handlers
#[inline(always)] #[inline(always)]
pub unsafe fn new() -> Self { pub unsafe fn new(token: usize) -> Self {
Self { _0: PhantomData } Self { token, _0: PhantomData }
}
pub const fn token(&self) -> usize {
self.token
} }
} }

View File

@ -5,13 +5,14 @@ use libsys::error::Errno;
// Device classes // Device classes
// pub mod fdt; // pub mod fdt;
// pub mod gpio; // pub mod gpio;
// pub mod irq; pub mod irq;
pub mod display;
// pub mod pci; // pub mod pci;
// pub mod rtc; // pub mod rtc;
// pub mod sd; // pub mod sd;
pub mod serial; pub mod serial;
// pub mod timer; pub mod timer;
// pub mod pseudo; pub mod pseudo;
// pub mod tty; // pub mod tty;
/// Generic device trait /// Generic device trait

View File

@ -1,8 +1,5 @@
use crate::arch::machine::{self, IrqNumber};
use crate::dev::{ use crate::dev::{
irq::{IntController, IntSource},
serial::SerialDevice, serial::SerialDevice,
tty::{CharRing, TtyDevice},
Device, Device,
}; };
use crate::mem::virt::DeviceMemoryIo; use crate::mem::virt::DeviceMemoryIo;

49
kernel/src/font.rs Normal file
View File

@ -0,0 +1,49 @@
use crate::util::InitOnce;
use libsys::mem::read_le32;
use crate::dev::display::FramebufferInfo;
static FONT_DATA: &[u8] = include_bytes!("../../etc/default8x16.psfu");
static FONT: InitOnce<Font> = InitOnce::new();
pub struct Font {
char_width: usize,
char_height: usize,
bytes_per_glyph: usize,
data: &'static [u8],
}
impl Font {
pub fn draw(&self, fb: &FramebufferInfo, bx: usize, by: usize, ch: char, fg: u32, bg: u32) {
if ch >= ' ' && ch < '\x7B' {
let char_data = &self.data[ch as usize * self.bytes_per_glyph..];
for iy in 0..self.char_height {
for ix in 0..self.char_width {
let cx = self.char_width - ix - 1;
let ptr = fb.virt_base + (ix + bx + (iy + by) * fb.width) * 4;
let value = if char_data[iy + (cx) / 8] & (1 << (cx & 0x7)) != 0 {
fg
} else {
bg
};
unsafe { core::ptr::write_volatile(ptr as *mut u32, value) }
}
}
}
}
}
pub fn init() {
assert_eq!(read_le32(&FONT_DATA[..]), 0x864ab572);
FONT.init(Font {
char_width: read_le32(&FONT_DATA[28..]) as usize,
char_height: read_le32(&FONT_DATA[24..]) as usize,
bytes_per_glyph: read_le32(&FONT_DATA[20..]) as usize,
data: &FONT_DATA[32..]
});
}
pub fn get() -> &'static Font {
FONT.get()
}

View File

@ -162,8 +162,8 @@ pub fn init() {
use crate::dev::timer::TimestampSource; use crate::dev::timer::TimestampSource;
let mut writer = BufferWriter::new(buf); let mut writer = BufferWriter::new(buf);
let time = machine::local_timer().timestamp()?; // let time = machine::local_timer().timestamp()?;
write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?; // write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?;
Ok(writer.count()) Ok(writer.count())
}); });
} }

View File

@ -11,8 +11,8 @@
const_panic, const_panic,
panic_info_message, panic_info_message,
alloc_error_handler, alloc_error_handler,
// linked_list_cursors, linked_list_cursors,
// const_btree_new, const_btree_new,
core_intrinsics, core_intrinsics,
const_generics_defaults, const_generics_defaults,
)] )]
@ -26,7 +26,7 @@ extern crate kernel_macros;
extern crate cfg_if; extern crate cfg_if;
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
// extern crate alloc; extern crate alloc;
#[macro_use] #[macro_use]
pub mod debug; pub mod debug;
@ -34,7 +34,8 @@ pub mod debug;
pub mod arch; pub mod arch;
pub mod config; pub mod config;
pub mod dev; pub mod dev;
// pub mod fs; pub mod fs;
pub mod font;
// pub mod init; // pub mod init;
pub mod mem; pub mod mem;
// pub mod proc; // pub mod proc;
@ -48,7 +49,7 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
// asm!("msr daifset, #2"); // asm!("msr daifset, #2");
// } // }
// errorln!("Panic: {:?}", pi); errorln!("Panic: {:?}", pi);
// TODO // TODO
loop {} loop {}
} }

View File

@ -9,7 +9,7 @@ mod manager;
mod reserved; mod reserved;
use manager::{Manager, SimpleManager, MANAGER}; use manager::{Manager, SimpleManager, MANAGER};
pub use reserved::ReservedRegion; pub use reserved::{ReservedRegion, reserve};
type ManagerImpl = SimpleManager; type ManagerImpl = SimpleManager;
@ -209,11 +209,14 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T)
mem_base = reg.start; mem_base = reg.start;
} }
} }
// infoln!("Memory base is {:#x}", mem_base); infoln!("Memory base is {:#x}", mem_base);
// Step 1. Count available memory // Step 1. Count available memory
let mut total_pages = 0usize; let mut total_pages = 0usize;
for reg in iter.clone() { for reg in iter.clone() {
total_pages += (reg.end - reg.start) / PAGE_SIZE; let upper = (reg.end - mem_base) / PAGE_SIZE;
if upper > total_pages {
total_pages = upper;
}
} }
// TODO maybe instead of size_of::<...> use Layout? // TODO maybe instead of size_of::<...> use Layout?
let need_pages = ((total_pages * size_of::<PageInfo>()) + 0xFFF) / 0x1000; let need_pages = ((total_pages * size_of::<PageInfo>()) + 0xFFF) / 0x1000;
@ -238,7 +241,7 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T)
} }
} }
} }
// infoln!("{}K of usable physical memory", usable_pages * 4); infoln!("{}K of usable physical memory", usable_pages * 4);
*MANAGER.lock() = Some(manager); *MANAGER.lock() = Some(manager);
} }

View File

@ -47,12 +47,12 @@ static mut RESERVED_REGION_PAGES: MaybeUninit<ReservedRegion> = MaybeUninit::uni
/// ///
/// Unsafe: `region` is passed as a raw pointer. /// Unsafe: `region` is passed as a raw pointer.
pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) { pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) {
// infoln!( infoln!(
// "Reserving {:?} region: {:#x}..{:#x}", "Reserving {:?} region: {:#x}..{:#x}",
// usage, usage,
// (*region).start, (*region).start,
// (*region).end (*region).end
// ); );
(*region).next = RESERVED_REGIONS_HEAD; (*region).next = RESERVED_REGIONS_HEAD;
RESERVED_REGIONS_HEAD = region; RESERVED_REGIONS_HEAD = region;
} }

View File

@ -20,6 +20,14 @@ impl<T> InitOnce<T> {
} }
} }
pub fn as_ref_option(&self) -> Option<&T> {
if self.is_initialized() {
Some(self.get())
} else {
None
}
}
/// Returns `true` if this [InitOnce<T>] can be used /// Returns `true` if this [InitOnce<T>] can be used
#[inline(always)] #[inline(always)]
pub fn is_initialized(&self) -> bool { pub fn is_initialized(&self) -> bool {
@ -29,7 +37,7 @@ impl<T> InitOnce<T> {
/// Returns the initialized value. Will panic if the value has not /// Returns the initialized value. Will panic if the value has not
/// yet been initialized. /// yet been initialized.
#[allow(clippy::mut_from_ref)] #[allow(clippy::mut_from_ref)]
pub fn get(&self) -> &mut T { pub fn get(&self) -> &T {
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>"); assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>");
unsafe { (*self.inner.get()).assume_init_mut() } unsafe { (*self.inner.get()).assume_init_mut() }
} }