feature: framebuffer console WIP
This commit is contained in:
parent
ec5b5bc31b
commit
d4323e3c8f
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -97,6 +97,7 @@ dependencies = [
|
||||
"kernel-macros",
|
||||
"libsys",
|
||||
"memfs",
|
||||
"multiboot2",
|
||||
"tock-registers",
|
||||
"vfs",
|
||||
]
|
||||
@ -162,6 +163,14 @@ dependencies = [
|
||||
"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]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
|
2
Makefile
2
Makefile
@ -31,7 +31,7 @@ QEMU_OPTS=-s
|
||||
ifeq ($(ARCH),x86_64)
|
||||
MACH=none
|
||||
QEMU_OPTS+=-cdrom $(O)/image.iso \
|
||||
-M q35,accel=kvm \
|
||||
-M q35 \
|
||||
-cpu host \
|
||||
-enable-kvm \
|
||||
-m 512 \
|
||||
|
BIN
etc/default8x16.psfu
Normal file
BIN
etc/default8x16.psfu
Normal file
Binary file not shown.
@ -22,5 +22,8 @@ SECTIONS {
|
||||
.bss : AT(. - KERNEL_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ bitflags = "^1.3.0"
|
||||
kernel-macros = { path = "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]
|
||||
cortex-a = { version = "6.x.x" }
|
||||
fdt-rs = { version = "0.x.x", default-features = false }
|
||||
|
@ -8,6 +8,14 @@
|
||||
.long ARCH
|
||||
.long HDRLEN
|
||||
.long CHKSUM
|
||||
|
||||
.short 5
|
||||
.short 0
|
||||
.long 20
|
||||
.long 800
|
||||
.long 600
|
||||
.long 32
|
||||
|
||||
.short 0
|
||||
.long 8
|
||||
|
||||
|
@ -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]
|
||||
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 {
|
||||
// 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();
|
||||
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));
|
||||
|
@ -2,57 +2,48 @@
|
||||
|
||||
.section .text._entry
|
||||
__x86_64_enter_upper:
|
||||
// Setup paging table
|
||||
mov $(PTE_PRESENT | PTE_BLOCK | PTE_WRITABLE | PTE_USERSPACE), %eax
|
||||
// Fill PD0: 0..1GiB
|
||||
lea (KERNEL_PD0 - KERNEL_OFFSET), %edi
|
||||
mov $512, %ecx
|
||||
1:
|
||||
dec %ecx
|
||||
|
||||
mov %ecx, %edx
|
||||
shl $21, %edx
|
||||
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
|
||||
mov $(PTE_PRESENT | PTE_WRITABLE | PTE_USERSPACE), %edx
|
||||
// Setup PML4
|
||||
lea (KERNEL_FIXED - KERNEL_OFFSET), %edi
|
||||
lea (KERNEL_FIXED + 4096 - KERNEL_OFFSET), %esi
|
||||
mov %edx, %eax
|
||||
or %esi, %eax
|
||||
// pml4[0] = %eax
|
||||
mov %eax, (%edi)
|
||||
// pml4[511] = %eax
|
||||
mov %eax, 4088(%edi)
|
||||
|
||||
// Setup PDPT
|
||||
mov $(PTE_PRESENT | PTE_WRITABLE | PTE_USERSPACE), %eax
|
||||
lea (KERNEL_PDPT - KERNEL_OFFSET), %edi
|
||||
mov %esi, %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
|
||||
or %eax, %esi
|
||||
mov %esi, (%edi)
|
||||
add $4096, %esi
|
||||
inc %ecx
|
||||
cmp $16, %ecx
|
||||
jne 1b
|
||||
|
||||
lea (KERNEL_PD1 - KERNEL_OFFSET), %esi
|
||||
or %eax, %esi
|
||||
mov %esi, 8(%edi)
|
||||
// Setup PDs
|
||||
lea (KERNEL_FIXED + 8192 - KERNEL_OFFSET), %edi
|
||||
mov $(PTE_PRESENT | PTE_BLOCK | PTE_WRITABLE), %edx
|
||||
mov $(512 * 16), %ecx
|
||||
1:
|
||||
dec %ecx
|
||||
|
||||
// Setup PML4
|
||||
lea (KERNEL_PML4 - KERNEL_OFFSET), %edi
|
||||
lea (KERNEL_PDPT - KERNEL_OFFSET), %esi
|
||||
or %eax, %esi
|
||||
mov %esi, (%edi)
|
||||
mov %esi, 4088(%edi)
|
||||
// %eax = attrs | (i << 21)
|
||||
mov %ecx, %eax
|
||||
shl $21, %eax
|
||||
or %edx, %eax
|
||||
|
||||
mov %eax, (%edi, %ecx, 8)
|
||||
|
||||
test %ecx, %ecx
|
||||
jnz 1b
|
||||
|
||||
// Enable PAE/PSE
|
||||
mov %cr4, %eax
|
||||
@ -66,6 +57,7 @@ __x86_64_enter_upper:
|
||||
wrmsr
|
||||
|
||||
// Set CR3
|
||||
lea (KERNEL_FIXED - KERNEL_OFFSET), %edi
|
||||
mov %edi, %cr3
|
||||
|
||||
// Enable paging
|
||||
|
0
kernel/src/arch/x86_64/display/efi_fb.rs
Normal file
0
kernel/src/arch/x86_64/display/efi_fb.rs
Normal file
@ -1,4 +1,6 @@
|
||||
use crate::arch::x86_64;
|
||||
use crate::debug::Level;
|
||||
use crate::dev::irq::{IrqContext, IntController};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExceptionFrame {
|
||||
@ -68,3 +70,11 @@ extern "C" fn __x86_64_exception_handler(frame: &mut ExceptionFrame) {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ struct Pointer {
|
||||
offset: usize
|
||||
}
|
||||
|
||||
const SIZE: usize = 256;
|
||||
pub const SIZE: usize = 256;
|
||||
|
||||
impl Entry {
|
||||
const PRESENT: u8 = 1 << 7;
|
||||
const INT32: u8 = 0xE;
|
||||
pub const PRESENT: u8 = 1 << 7;
|
||||
pub const INT32: u8 = 0xE;
|
||||
|
||||
pub const fn new(base: usize, selector: u16, flags: u8) -> 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);
|
||||
}
|
||||
|
||||
f(&mut IDT);
|
||||
|
||||
let idtr = Pointer {
|
||||
limit: size_of_val(&IDT) as u16 - 1,
|
||||
offset: &IDT as *const _ as usize
|
||||
|
144
kernel/src/arch/x86_64/intc.rs
Normal file
144
kernel/src/arch/x86_64/intc.rs
Normal 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));
|
83
kernel/src/arch/x86_64/irq_vectors.S
Normal file
83
kernel/src/arch/x86_64/irq_vectors.S
Normal 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
|
@ -1,7 +1,10 @@
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::dev::{serial::SerialDevice, display::StaticFramebuffer, irq::IntController};
|
||||
|
||||
mod uart;
|
||||
use uart::Uart;
|
||||
mod intc;
|
||||
use intc::I8259;
|
||||
|
||||
mod io;
|
||||
pub(self) use io::PortIo;
|
||||
|
||||
@ -18,7 +21,9 @@ pub(self) mod exception;
|
||||
/// Unsafe: disables IRQ handling temporarily
|
||||
#[inline(always)]
|
||||
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
|
||||
@ -29,7 +34,13 @@ pub unsafe fn irq_mask_save() -> u64 {
|
||||
/// conjunction with [irq_mask_save]
|
||||
#[inline(always)]
|
||||
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 {
|
||||
@ -37,3 +48,5 @@ pub fn console() -> &'static impl SerialDevice {
|
||||
}
|
||||
|
||||
static COM1: Uart = unsafe { Uart::new(0x3F8) };
|
||||
static INTC: I8259 = I8259::new();
|
||||
pub(self) static DISPLAY: StaticFramebuffer = StaticFramebuffer::uninit();
|
||||
|
@ -1,13 +1,22 @@
|
||||
use crate::mem::virt::AddressSpace;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use libsys::error::Errno;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct Entry(u64);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, align(0x1000))]
|
||||
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)]
|
||||
@ -15,14 +24,11 @@ pub struct Space(Table);
|
||||
|
||||
// Upper mappings
|
||||
#[no_mangle]
|
||||
static KERNEL_PDPT: Table = Table::empty();
|
||||
#[no_mangle]
|
||||
static KERNEL_PD0: Table = Table::empty();
|
||||
#[no_mangle]
|
||||
static KERNEL_PD1: Table = Table::empty();
|
||||
|
||||
#[no_mangle]
|
||||
static KERNEL_PML4: Space = Space::empty();
|
||||
static mut KERNEL_FIXED: FixedTableGroup = FixedTableGroup {
|
||||
pml4: Table::empty(),
|
||||
pdpt: Table::empty(),
|
||||
pd: [Table::empty(); 16]
|
||||
};
|
||||
|
||||
impl Entry {
|
||||
const fn invalid() -> Self {
|
||||
@ -61,6 +67,27 @@ impl AddressSpace for Space {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable() -> Result<(), Errno> {
|
||||
loop {}
|
||||
impl Index<usize> for Table {
|
||||
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(())
|
||||
}
|
||||
|
@ -11,12 +11,84 @@
|
||||
//! * [warnln!]
|
||||
//! * [errorln!]
|
||||
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use libsys::{debug::TraceLevel, error::Errno};
|
||||
use crate::dev::{
|
||||
display::{Display, FramebufferInfo},
|
||||
serial::SerialDevice,
|
||||
};
|
||||
use crate::font;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use libsys::{debug::TraceLevel, error::Errno};
|
||||
|
||||
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
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
@ -42,7 +114,7 @@ impl TryFrom<u32> for Level {
|
||||
2 => Ok(Level::Info),
|
||||
3 => Ok(Level::Warn),
|
||||
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 fmt::Write;
|
||||
|
||||
if level > Level::Debug {
|
||||
DISPLAY.lock().write_fmt(args).ok();
|
||||
}
|
||||
|
||||
if level >= LEVEL {
|
||||
SerialOutput {
|
||||
inner: machine::console(),
|
||||
@ -145,3 +221,89 @@ pub fn _debug(level: Level, args: fmt::Arguments) {
|
||||
.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
58
kernel/src/dev/display.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ use libsys::error::Errno;
|
||||
|
||||
/// Token to indicate the local core is running in IRQ context
|
||||
pub struct IrqContext<'irq_context> {
|
||||
token: usize,
|
||||
_0: PhantomData<&'irq_context ()>,
|
||||
}
|
||||
|
||||
@ -45,7 +46,11 @@ impl<'q> IrqContext<'q> {
|
||||
///
|
||||
/// Only allowed to be constructed in top-level IRQ handlers
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
Self { _0: PhantomData }
|
||||
pub unsafe fn new(token: usize) -> Self {
|
||||
Self { token, _0: PhantomData }
|
||||
}
|
||||
|
||||
pub const fn token(&self) -> usize {
|
||||
self.token
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,14 @@ use libsys::error::Errno;
|
||||
// Device classes
|
||||
// pub mod fdt;
|
||||
// pub mod gpio;
|
||||
// pub mod irq;
|
||||
pub mod irq;
|
||||
pub mod display;
|
||||
// pub mod pci;
|
||||
// pub mod rtc;
|
||||
// pub mod sd;
|
||||
pub mod serial;
|
||||
// pub mod timer;
|
||||
// pub mod pseudo;
|
||||
pub mod timer;
|
||||
pub mod pseudo;
|
||||
// pub mod tty;
|
||||
|
||||
/// Generic device trait
|
||||
|
@ -1,8 +1,5 @@
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
tty::{CharRing, TtyDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
|
49
kernel/src/font.rs
Normal file
49
kernel/src/font.rs
Normal 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()
|
||||
}
|
@ -162,8 +162,8 @@ pub fn init() {
|
||||
use crate::dev::timer::TimestampSource;
|
||||
|
||||
let mut writer = BufferWriter::new(buf);
|
||||
let time = machine::local_timer().timestamp()?;
|
||||
write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?;
|
||||
// let time = machine::local_timer().timestamp()?;
|
||||
// write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?;
|
||||
Ok(writer.count())
|
||||
});
|
||||
}
|
||||
|
@ -11,8 +11,8 @@
|
||||
const_panic,
|
||||
panic_info_message,
|
||||
alloc_error_handler,
|
||||
// linked_list_cursors,
|
||||
// const_btree_new,
|
||||
linked_list_cursors,
|
||||
const_btree_new,
|
||||
core_intrinsics,
|
||||
const_generics_defaults,
|
||||
)]
|
||||
@ -26,7 +26,7 @@ extern crate kernel_macros;
|
||||
extern crate cfg_if;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
// extern crate alloc;
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
@ -34,7 +34,8 @@ pub mod debug;
|
||||
pub mod arch;
|
||||
pub mod config;
|
||||
pub mod dev;
|
||||
// pub mod fs;
|
||||
pub mod fs;
|
||||
pub mod font;
|
||||
// pub mod init;
|
||||
pub mod mem;
|
||||
// pub mod proc;
|
||||
@ -48,7 +49,7 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
// asm!("msr daifset, #2");
|
||||
// }
|
||||
|
||||
// errorln!("Panic: {:?}", pi);
|
||||
errorln!("Panic: {:?}", pi);
|
||||
// TODO
|
||||
loop {}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ mod manager;
|
||||
mod reserved;
|
||||
|
||||
use manager::{Manager, SimpleManager, MANAGER};
|
||||
pub use reserved::ReservedRegion;
|
||||
pub use reserved::{ReservedRegion, reserve};
|
||||
|
||||
type ManagerImpl = SimpleManager;
|
||||
|
||||
@ -209,11 +209,14 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T)
|
||||
mem_base = reg.start;
|
||||
}
|
||||
}
|
||||
// infoln!("Memory base is {:#x}", mem_base);
|
||||
infoln!("Memory base is {:#x}", mem_base);
|
||||
// Step 1. Count available memory
|
||||
let mut total_pages = 0usize;
|
||||
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?
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,12 @@ static mut RESERVED_REGION_PAGES: MaybeUninit<ReservedRegion> = MaybeUninit::uni
|
||||
///
|
||||
/// Unsafe: `region` is passed as a raw pointer.
|
||||
pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) {
|
||||
// infoln!(
|
||||
// "Reserving {:?} region: {:#x}..{:#x}",
|
||||
// usage,
|
||||
// (*region).start,
|
||||
// (*region).end
|
||||
// );
|
||||
infoln!(
|
||||
"Reserving {:?} region: {:#x}..{:#x}",
|
||||
usage,
|
||||
(*region).start,
|
||||
(*region).end
|
||||
);
|
||||
(*region).next = RESERVED_REGIONS_HEAD;
|
||||
RESERVED_REGIONS_HEAD = region;
|
||||
}
|
||||
|
@ -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
|
||||
#[inline(always)]
|
||||
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
|
||||
/// yet been initialized.
|
||||
#[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>");
|
||||
unsafe { (*self.inner.get()).assume_init_mut() }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user