Compare commits
5 Commits
eb1e469fdc
...
314189882b
Author | SHA1 | Date |
---|---|---|
fekhesk | 314189882b | |
fekhesk | d11d5ddd97 | |
fekhesk | 8af40d0414 | |
fekhesk | 600ba7163a | |
fekhesk | 8fcc70bee1 |
|
@ -37,3 +37,4 @@ Cargo.lock
|
|||
/boot.o
|
||||
/build/
|
||||
/OVMF-pure-efi.fd
|
||||
/isodir
|
|
@ -5,6 +5,11 @@
|
|||
<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>
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -3,22 +3,21 @@ name = "wukkOS"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
spin = "0.9.1"
|
||||
x86_64 = "0.14.10"
|
||||
x2apic = "0.4.1"
|
||||
rlibc = "1.0"
|
||||
multiboot2 = { version = "0.14.0", optional = true }
|
||||
limine = { version = "0.1.9", optional = true }
|
||||
acpi = { version = "4.1.1", optional = true }
|
||||
linked_list_allocator = { version = "0.9.0", optional = true }
|
||||
pc-keyboard = "0.6.1"
|
||||
[dependencies.lazy_static]
|
||||
version = "1.4.0"
|
||||
features = ["spin_no_std"]
|
||||
|
||||
[features]
|
||||
default = ["f_multiboot2", "f_ll_alloc", "f_debug_verbose"]
|
||||
default = ["f_limine", "f_ll_alloc"]#, "f_debug_verbose"]
|
||||
f_debug_verbose = []
|
||||
f_multiboot2 = ["dep:multiboot2", "dep:acpi"]
|
||||
f_limine = ["dep:limine", "dep:acpi"]
|
||||
f_ll_alloc = ["dep:linked_list_allocator"]
|
32
Makefile
32
Makefile
|
@ -1,26 +1,30 @@
|
|||
arch ?= x86_64
|
||||
kernel := target/$(arch)-custom/debug/libwukkOS.a
|
||||
kernel := target/$(arch)-custom/debug/wukkOS
|
||||
iso := build/arch/$(arch)/wukkOS.iso
|
||||
target ?= $(arch)-custom
|
||||
final := build/arch/$(arch)/wukkOS.bin
|
||||
efi_bios := build/arch/$(arch)/OVMF-pure-efi.fd
|
||||
gcc ?= gcc
|
||||
ld ?= ld
|
||||
|
||||
linker_script := arch/$(arch)/linker.ld
|
||||
grub_cfg := arch/$(arch)/grub.cfg
|
||||
bootloader_cfg := arch/$(arch)/limine.cfg
|
||||
assembly_source_files := $(wildcard arch/$(arch)/*.asm)
|
||||
assembly_object_files := $(patsubst arch/$(arch)/%.asm, \
|
||||
build/arch/$(arch)/%.o, $(assembly_source_files))
|
||||
|
||||
.PHONY: all clean run iso quick_invalidate
|
||||
.PHONY: all clean run iso quick_invalidate build_no_iso
|
||||
|
||||
all: $(final) $(iso)
|
||||
|
||||
build_no_iso: $(final)
|
||||
|
||||
clean:
|
||||
@xargo clean
|
||||
@rm -rf build
|
||||
|
||||
run: $(final) $(iso)
|
||||
@qemu-system-$(arch) -bios $(efi_bios) -cdrom $(iso) \
|
||||
@qemu-system-$(arch) -bios $(efi_bios) -cdrom $(iso) -d int -D qemulog.log \
|
||||
-chardev stdio,id=char0,mux=on,logfile=serial.log,signal=off \
|
||||
-serial chardev:char0 -mon chardev=char0 -m 512M
|
||||
|
||||
|
@ -33,18 +37,26 @@ iso: $(iso)
|
|||
|
||||
$(iso): $(final) $(grub_cfg)
|
||||
@cp OVMF-pure-efi.fd build/arch/$(arch)/OVMF-pure-efi.fd # TODO! remove this, it's only for testing and i don't think we can distribute it
|
||||
@mkdir -p isodir/boot/grub
|
||||
@mkdir -p isodir/boot
|
||||
@cp $(final) isodir/boot/wukkOS.bin
|
||||
@cp $(grub_cfg) isodir/boot/grub/grub.cfg
|
||||
@grub-mkrescue -o $(iso) isodir
|
||||
@cp $(bootloader_cfg) isodir/boot/limine.cfg
|
||||
@cp byob/limine.sys byob/limine-cd.bin byob/limine-cd-efi.bin isodir/boot/
|
||||
@xorriso -as mkisofs -b boot/limine-cd.bin \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
--efi-boot boot/limine-cd-efi.bin \
|
||||
-efi-boot-part --efi-boot-image --protective-msdos-label \
|
||||
isodir -o $(iso)
|
||||
@rm -rf isodir
|
||||
@byob/limine-deploy $(iso)
|
||||
|
||||
$(final): $(kernel) $(linker_script) $(assembly_object_files)
|
||||
@ld -n -T $(linker_script) -o $(final) $(assembly_object_files) $(kernel) \
|
||||
--gc-sections
|
||||
@mkdir -p $(shell dirname $@)
|
||||
@cp $(kernel) $(final)
|
||||
#@$(ld) -n -T $(linker_script) -o $(final) $(kernel) \
|
||||
# --gc-sections
|
||||
|
||||
$(kernel):
|
||||
@RUST_TARGET_PATH=$(shell pwd) xargo build --target $(target) -Zbuild-std=core,alloc --features "f_multiboot2"
|
||||
@RUST_TARGET_PATH=$(shell pwd) xargo build --target $(target) -Zbuild-std=core,alloc --features "f_limine"
|
||||
|
||||
build/arch/$(arch)/%.o: arch/$(arch)/%.asm
|
||||
@mkdir -p $(shell dirname $@)
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
global start
|
||||
extern long_mode_start
|
||||
|
||||
section .text
|
||||
bits 32
|
||||
|
||||
;; basic boilerplate stuff from blog_os, expect to be changed
|
||||
|
||||
start:
|
||||
mov esp, stack_top
|
||||
mov edi, ebx
|
||||
|
||||
call check_multiboot
|
||||
call check_cpuid
|
||||
call check_long_mode
|
||||
|
||||
call setup_page_tables
|
||||
call enable_paging
|
||||
|
||||
lgdt [gdt64.pointer]
|
||||
|
||||
jmp gdt64.code:long_mode_start
|
||||
|
||||
hlt
|
||||
check_multiboot:
|
||||
cmp eax, 0x36d76289
|
||||
jne .no_multiboot
|
||||
ret
|
||||
.no_multiboot:
|
||||
mov al, "0"
|
||||
jmp error
|
||||
check_cpuid:
|
||||
; Check if CPUID is supported by attempting to flip the ID bit (bit 21)
|
||||
; in the FLAGS register. If we can flip it, CPUID is available.
|
||||
|
||||
; Copy FLAGS in to EAX via stack
|
||||
pushfd
|
||||
pop eax
|
||||
|
||||
; Copy to ECX as well for comparing later on
|
||||
mov ecx, eax
|
||||
|
||||
; Flip the ID bit
|
||||
xor eax, 1 << 21
|
||||
|
||||
; Copy EAX to FLAGS via the stack
|
||||
push eax
|
||||
popfd
|
||||
|
||||
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
|
||||
pushfd
|
||||
pop eax
|
||||
|
||||
; Restore FLAGS from the old version stored in ECX (i.e. flipping the
|
||||
; ID bit back if it was ever flipped).
|
||||
push ecx
|
||||
popfd
|
||||
|
||||
; Compare EAX and ECX. If they are equal then that means the bit
|
||||
; wasn't flipped, and CPUID isn't supported.
|
||||
cmp eax, ecx
|
||||
je .no_cpuid
|
||||
ret
|
||||
.no_cpuid:
|
||||
mov al, "1"
|
||||
jmp error
|
||||
check_long_mode:
|
||||
; test if extended processor info in available
|
||||
mov eax, 0x80000000 ; implicit argument for cpuid
|
||||
cpuid ; get highest supported argument
|
||||
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
|
||||
jb .no_long_mode ; if it's less, the CPU is too old for long mode
|
||||
|
||||
; use extended info to test if long mode is available
|
||||
mov eax, 0x80000001 ; argument for extended processor info
|
||||
cpuid ; returns various feature bits in ecx and edx
|
||||
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
|
||||
jz .no_long_mode ; If it's not set, there is no long mode
|
||||
ret
|
||||
.no_long_mode:
|
||||
mov al, "2"
|
||||
jmp error
|
||||
setup_page_tables:
|
||||
; map first P4 entry to P3 table
|
||||
mov eax, p3_table
|
||||
or eax, 0b11 ; present + writable
|
||||
mov [p4_table], eax
|
||||
|
||||
; map first P3 entry to P2 table
|
||||
mov eax, p2_table
|
||||
or eax, 0b11 ; present + writable
|
||||
mov [p3_table], eax
|
||||
|
||||
; map each P2 entry to a huge 2MiB page
|
||||
mov ecx, 0 ; counter variable
|
||||
|
||||
.map_p2_table:
|
||||
; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx
|
||||
mov eax, 0x200000 ; 2MiB
|
||||
mul ecx ; start address of ecx-th page
|
||||
or eax, 0b10000011 ; present + writable + huge
|
||||
mov [p2_table + ecx * 8], eax ; map ecx-th entry
|
||||
|
||||
inc ecx ; increase counter
|
||||
cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped
|
||||
jne .map_p2_table ; else map the next entry
|
||||
|
||||
ret
|
||||
|
||||
enable_paging:
|
||||
; load P4 to cr3 register (cpu uses this to access the P4 table)
|
||||
mov eax, p4_table
|
||||
mov cr3, eax
|
||||
|
||||
; enable PAE-flag in cr4 (Physical Address Extension)
|
||||
mov eax, cr4
|
||||
or eax, 1 << 5
|
||||
mov cr4, eax
|
||||
|
||||
; set the long mode bit in the EFER MSR (model specific register)
|
||||
mov ecx, 0xC0000080
|
||||
rdmsr
|
||||
or eax, 1 << 8
|
||||
wrmsr
|
||||
|
||||
; enable paging in the cr0 register
|
||||
mov eax, cr0
|
||||
or eax, 1 << 31
|
||||
mov cr0, eax
|
||||
|
||||
ret
|
||||
|
||||
; Prints `ERR: ` and the given error code to screen and hangs.
|
||||
; parameter: error code (in ascii) in al
|
||||
error:
|
||||
mov dword [0xb8000], 0x4f524f45
|
||||
mov dword [0xb8004], 0x4f3a4f52
|
||||
mov dword [0xb8008], 0x4f204f20
|
||||
mov byte [0xb800a], al
|
||||
hlt
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
p4_table:
|
||||
resb 4096
|
||||
p3_table:
|
||||
resb 4096
|
||||
p2_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
resb 4096 * 4 ;; 16 KiB
|
||||
stack_top:
|
||||
|
||||
section .rodata
|
||||
gdt64:
|
||||
dq 0 ; zero entry
|
||||
.code: equ $ - gdt64 ; new
|
||||
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
|
||||
.pointer:
|
||||
dw $ - gdt64 - 1
|
||||
dq gdt64
|
|
@ -1,7 +0,0 @@
|
|||
set timeout=0
|
||||
set default=0
|
||||
|
||||
menuentry "wukkOS basic kernel" {
|
||||
multiboot2 /boot/wukkOS.bin
|
||||
boot
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
TIMEOUT=0
|
||||
|
||||
:wukkOS
|
||||
PROTOCOL=limine
|
||||
KERNEL_PATH=boot:///boot/wukkOS.bin
|
|
@ -1,26 +1,48 @@
|
|||
ENTRY(start)
|
||||
/* Tell the linker that we want an x86_64 ELF64 output file */
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
OUTPUT_ARCH(i386:x86-64)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
/* We want the symbol `x86_64_barebones_main` to be our entry point */
|
||||
ENTRY(kernel_main)
|
||||
|
||||
.boot :
|
||||
{
|
||||
/* ensure that the multiboot header is at the beginning */
|
||||
KEEP(*(.multiboot_header))
|
||||
}
|
||||
/* Define the program headers we want so the bootloader gives us the right */
|
||||
/* MMU permissions */
|
||||
PHDRS
|
||||
{
|
||||
null PT_NULL FLAGS(0) ; /* Null segment */
|
||||
text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */
|
||||
rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */
|
||||
data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */
|
||||
}
|
||||
|
||||
.text :
|
||||
{
|
||||
SECTIONS
|
||||
{
|
||||
/* We wanna be placed in the topmost 2GiB of the address space, for optimisations */
|
||||
/* and because that is what the Limine spec mandates. */
|
||||
/* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
|
||||
/* that is the beginning of the region. */
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
}
|
||||
} :text
|
||||
|
||||
.rodata :
|
||||
{
|
||||
/* Move to the next memory page for .rodata */
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
} :rodata
|
||||
|
||||
.data.rel.ro :
|
||||
{
|
||||
*(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*)
|
||||
}
|
||||
/* Move to the next memory page for .data */
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss .bss.*)
|
||||
} :data
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
global long_mode_start
|
||||
extern kernel_main
|
||||
|
||||
section .text
|
||||
bits 64
|
||||
long_mode_start:
|
||||
mov ax, 0x00
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
extern kernel_main
|
||||
call kernel_main
|
||||
|
||||
hlt
|
|
@ -1,16 +0,0 @@
|
|||
section .multiboot_header
|
||||
header_start:
|
||||
align 4
|
||||
; multiboot 2 magic
|
||||
dd 0xe85250d6
|
||||
dd 0x00000000
|
||||
dd header_end - header_start
|
||||
; checksum
|
||||
dd 0x100000000 - (0xe85250d6 + 0x00000000 + (header_end - header_start))
|
||||
|
||||
; required end tag
|
||||
dw 0 ; type
|
||||
dw 0 ; flags
|
||||
dd 8 ; size
|
||||
|
||||
header_end:
|
|
@ -0,0 +1,5 @@
|
|||
# limine binaries
|
||||
limine.sys
|
||||
limine-cd.bin
|
||||
limine-cd-efi.bin
|
||||
limine-deploy
|
|
@ -0,0 +1,9 @@
|
|||
# bring your own bootloader!
|
||||
wukkOS is currently reliant on limine, and it is also currently required for you to
|
||||
provide your own binaries for it. those binaries should be placed in this directory,
|
||||
and the Makefile will copy them into the iso.
|
||||
currently the binaries are:
|
||||
- limine.sys
|
||||
- limine-cd.bin
|
||||
- limine-cd-efi.bin
|
||||
- limine-deploy (should be for your current system)
|
39
serial.log
39
serial.log
|
@ -1,12 +1,6 @@
|
|||
[2J[01;01H[=3h[2J[01;01H[2J[01;01H[=3h[2J[01;01H[2J[01;01H[=3h[2J[01;01H[2J[01;01H[=3h[2J[01;01HBdsDxe: loading Boot0001 "UEFI QEMU DVD-ROM QM00003 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Master,0x0)
|
||||
BdsDxe: starting Boot0001 "UEFI QEMU DVD-ROM QM00003 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Master,0x0)
|
||||
[0m[30m[47mWelcome to GRUB!
|
||||
|
||||
[0m[37m[40m[0m[30m[40m[2J[01;01H[0m[37m[40m Booting `wukkOS basic kernel'
|
||||
|
||||
WARNING: no console will be available to OS
|
||||
error: no suitable video mode found.
|
||||
using serial port 0 as console
|
||||
[2J[01;01H[01;01Husing serial port 0 as console
|
||||
debug: setup GDT
|
||||
debug: GDT loaded
|
||||
debug: CS set
|
||||
|
@ -23,28 +17,9 @@ initialising frame allocator...[OK]
|
|||
initialising heap...[OK]
|
||||
testing heap...[OK]
|
||||
checking for apic compatibility...[OK]
|
||||
disabling PIC...[OK]
|
||||
initialising apic...[debug] read_phys_memory32: addr fee000f0 not mapped
|
||||
[debug] allocated frame: PhysFrame[4KiB](0x1c000)
|
||||
[debug] mapped page: Page[4KiB](0xfee00000)
|
||||
[debug] map_to_result: Ok(MapperFlush(Page[4KiB](0xfee00000)))
|
||||
[OK]
|
||||
setting up apic interrupts...[debug] write_phys_memory32: addr fec00000 not mapped
|
||||
[debug] allocated frame: PhysFrame[4KiB](0x1f000)
|
||||
[debug] mapped page: Page[4KiB](0xfec00000)
|
||||
[debug] map_to_result: Ok(MapperFlush(Page[4KiB](0xfec00000)))
|
||||
[OK]
|
||||
[debug] keyboard interrupt
|
||||
---KERNEL FUCKY WUKKY UWU---
|
||||
double fault!
|
||||
stack frame: InterruptStackFrame {
|
||||
instruction_pointer: VirtAddr(
|
||||
0x120ef1,
|
||||
),
|
||||
code_segment: 8,
|
||||
cpu_flags: 0x200002,
|
||||
stack_pointer: VirtAddr(
|
||||
0x140c00,
|
||||
),
|
||||
stack_segment: 16,
|
||||
}
|
||||
initialising apic...[OK]
|
||||
setting up apic interrupts...[OK]
|
||||
|
||||
hello world i am typing this using the legacy keyboard driver thingy in the apic from ioapic irq 1!
|
||||
wukkOS moment, probably gonna swap this out for a real keyboard driver eventually but this is funny so (:
|
||||
top ten wukkooOS moments
|
159
src/boot/mod.rs
159
src/boot/mod.rs
|
@ -1,127 +1,88 @@
|
|||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr::NonNull;
|
||||
use acpi::{AcpiHandler, InterruptModel, PhysicalMapping};
|
||||
use crate::{debug, KernelArgs, println};
|
||||
use acpi::{AcpiHandler, AcpiTables, InterruptModel, PhysicalMapping};
|
||||
use acpi::platform::interrupt::InterruptSourceOverride;
|
||||
use limine::{LimineBootInfoRequest, LimineKernelAddressRequest, LimineMemmapRequest, LimineTerminalRequest, LimineTerminalResponse, LimineRsdpRequest, LimineSmpRequest};
|
||||
use crate::{debug, println};
|
||||
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
use multiboot2::{load, MemoryMapTag, BootInformation};
|
||||
use x86_64::structures::paging::{FrameAllocator, Mapper, OffsetPageTable, Translate};
|
||||
use crate::memory::{BootInfoFrameAllocator, FRAME_ALLOC, MEM_MAPPER};
|
||||
use x86_64::structures::paging::{FrameAllocator, Mapper, OffsetPageTable, Page, Translate};
|
||||
use x86_64::VirtAddr;
|
||||
use crate::memory::{BootInfoFrameAllocator, FRAME_ALLOC, MEM_MAPPER, PageSize, read_phys_memory32, VIRT_MEM_OFFSET};
|
||||
use crate::serial::terminal::ST;
|
||||
|
||||
pub struct KernelInfo {
|
||||
kernel_start: u64,
|
||||
kernel_end: u64,
|
||||
safe_mem_start: u64,
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
boot_info: BootInformation,
|
||||
}
|
||||
pub static BOOTLOADER_INFO: LimineBootInfoRequest = LimineBootInfoRequest::new(0);
|
||||
pub static TERMINAL_REQUEST: LimineTerminalRequest = LimineTerminalRequest::new(0);
|
||||
pub static MEM_MAP: LimineMemmapRequest = LimineMemmapRequest::new(0);
|
||||
pub static RSDP_REQUEST: LimineRsdpRequest = LimineRsdpRequest::new(0);
|
||||
pub static KERNEL_ADDRESS: LimineKernelAddressRequest = LimineKernelAddressRequest::new(0);
|
||||
pub static SMP_REQUEST: LimineSmpRequest = LimineSmpRequest::new(0);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Handler;
|
||||
impl AcpiHandler for Handler {
|
||||
unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T> {
|
||||
// todo! check if size is too big
|
||||
debug!("read_phys_memory32: addr {:x} not mapped", physical_address);
|
||||
// map the page
|
||||
let frame = FRAME_ALLOC.lock().as_mut().unwrap().allocate_frame().unwrap();
|
||||
debug!("allocated frame: {:?}", frame);
|
||||
let flags = x86_64::structures::paging::PageTableFlags::PRESENT | x86_64::structures::paging::PageTableFlags::WRITABLE;
|
||||
let page = x86_64::structures::paging::Page::containing_address(initaladdr);
|
||||
debug!("mapped page: {:?}", page);
|
||||
let map_to_result = unsafe { MEM_MAPPER.lock().as_mut().unwrap().map_to(page, frame, flags, FRAME_ALLOC.lock().as_mut().unwrap()) };
|
||||
debug!("map_to_result: {:?}", map_to_result);
|
||||
if map_to_result.is_err() {
|
||||
panic!("Failed to map page");
|
||||
}
|
||||
let addr = unsafe { MEM_MAPPER.lock().as_mut().unwrap().translate_addr(initaladdr) };
|
||||
if let Some(addr) = addr {
|
||||
// physical start, virtual start, region length, mapped length, Self
|
||||
PhysicalMapping::new(
|
||||
physical_address,
|
||||
NonNull::new_unchecked(addr.as_u64() as *mut T),
|
||||
size, size,
|
||||
Self)
|
||||
} else {
|
||||
panic!("Failed to map page");
|
||||
}
|
||||
unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T> { // todo! check if size is too big
|
||||
// only get lower 32 bits of physical address
|
||||
let physical_address = physical_address as u32;
|
||||
debug!("mapping physical region: {:x} - {:x}", physical_address, physical_address + size as u32);
|
||||
let _ = read_phys_memory32(physical_address as u32) as usize;
|
||||
PhysicalMapping::new(
|
||||
physical_address as usize,
|
||||
NonNull::new_unchecked(physical_address as *mut T),
|
||||
size, size,
|
||||
Self)
|
||||
}
|
||||
|
||||
fn unmap_physical_region<T>(region: &PhysicalMapping<Self, T>) {
|
||||
// get page
|
||||
let page = x86_64::structures::paging::Page::containing_address(region.start_address());
|
||||
let page: Page<PageSize> = Page::containing_address(VirtAddr::new(region.physical_start() as u64));
|
||||
// unmap page
|
||||
let res = unsafe { MEM_MAPPER.lock().as_mut().unwrap().unmap(page) };
|
||||
// it isn't *that* important if we don't unmap successfully at the moment, so just write a warning if we fail
|
||||
if res.is_err() {
|
||||
println!("[WARN] failed to unmap page");
|
||||
|
||||
if let Err(e) = res {
|
||||
debug!("(THIS IS NORMAL) failed to unmap physical region: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KernelInfo {
|
||||
pub fn init_from_kernel_args(args: KernelArgs) -> Self {
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
{
|
||||
let boot_info = unsafe { load(args.multiboot_information_address) }.expect("failed to load multiboot2 information");
|
||||
let elf_sections = boot_info.elf_sections_tag().expect("no elf sections tag");
|
||||
let kernel_start = elf_sections.sections().map(|s| s.start_address()).min().unwrap();
|
||||
let kernel_end = elf_sections.sections().map(|s| s.end_address()).max().unwrap();
|
||||
// get end of multiboot for safe memory
|
||||
let safe_mem_start = boot_info.start_address() + boot_info.total_size();
|
||||
let kernel_info = KernelInfo {
|
||||
kernel_start,
|
||||
kernel_end,
|
||||
safe_mem_start: safe_mem_start as u64,
|
||||
boot_info,
|
||||
};
|
||||
kernel_info
|
||||
}
|
||||
}
|
||||
pub struct LimineWriter;
|
||||
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
pub fn get_memory_tag(&self) -> &MemoryMapTag {
|
||||
let mm_tag = self.boot_info.memory_map_tag().expect("no memory map tag").clone();
|
||||
mm_tag
|
||||
}
|
||||
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
pub fn memory_areas(&self) -> impl Iterator<Item = &multiboot2::MemoryArea> {
|
||||
let mm_tag = self.boot_info.memory_map_tag().expect("ERR NO MEM MAP TAG!");
|
||||
mm_tag.all_memory_areas()
|
||||
}
|
||||
|
||||
pub fn is_safe_memory(&self, addr: u64) -> bool {
|
||||
addr >= self.safe_mem_start && addr >= self.kernel_end
|
||||
}
|
||||
|
||||
pub fn safe_memory_start(&self) -> u64 {
|
||||
self.safe_mem_start
|
||||
}
|
||||
|
||||
pub fn acpi_get_ioapic_addr(&self) -> u32 {
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
{
|
||||
let acpi_tag = self.boot_info.rsdp_v1_tag().expect("no acpi tag");
|
||||
let rsdp = acpi_tag;
|
||||
let rsdp = unsafe { &*rsdp };
|
||||
let rsdt = rsdp.rsdt_address();
|
||||
let rsdt = unsafe {
|
||||
acpi::AcpiTables::from_rsdt(
|
||||
AcpiHandler, 0,
|
||||
rsdt)
|
||||
.expect("failed to get acpi tables")
|
||||
};
|
||||
let platform_info = rsdt.platform_info().expect("failed to get platform info");
|
||||
let interrupt_model = platform_info.interrupt_model;
|
||||
if let InterruptModel::Apic(apic) = interrupt_model {
|
||||
let ioapics = apic.io_apics;
|
||||
let ioapic = ioapics.first().expect("no ioapics");
|
||||
let ioapic_addr = ioapic.address;
|
||||
ioapic_addr
|
||||
impl core::fmt::Write for LimineWriter {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
static mut CACHED: Option<&'static LimineTerminalResponse> = None;
|
||||
unsafe {
|
||||
if let Some(writer) = CACHED {
|
||||
let terminal = &writer.terminals()[0];
|
||||
writer.write().unwrap()(terminal, s);
|
||||
} else {
|
||||
panic!("no ioapic");
|
||||
let response = TERMINAL_REQUEST.get_response().get().unwrap();
|
||||
let terminal = &response.terminals()[0];
|
||||
let writer = response.write().unwrap();
|
||||
writer(terminal, s);
|
||||
|
||||
CACHED = Some(response);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ioapic_info() -> (u32, Vec<InterruptSourceOverride>) {
|
||||
let rsdp = RSDP_REQUEST.get_response().get().unwrap();
|
||||
let rsdp_ptr = rsdp.address.get().unwrap() as *const u8;
|
||||
let tables = unsafe { AcpiTables::from_rsdp(Handler, rsdp_ptr as usize).unwrap() };
|
||||
let platform_info = tables.platform_info().expect("no platform info");
|
||||
let interrupt_model = platform_info.interrupt_model;
|
||||
let apic = match interrupt_model {
|
||||
InterruptModel::Apic(apic) => apic,
|
||||
_ => panic!("unsupported interrupt model"),
|
||||
};
|
||||
let ioapic = apic.io_apics.first().expect("no ioapic");
|
||||
let address = ioapic.address;
|
||||
let overrides = apic.interrupt_source_overrides;
|
||||
(address, overrides)
|
||||
}
|
|
@ -1,13 +1,46 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::arch::asm;
|
||||
use x86_64::{PhysAddr, VirtAddr};
|
||||
use acpi::platform::interrupt::InterruptSourceOverride;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use x2apic::ioapic::{IoApic, IrqFlags, IrqMode, RedirectionTableEntry};
|
||||
use x2apic::lapic::{LocalApic, LocalApicBuilder, xapic_base};
|
||||
use x86_64::PhysAddr;
|
||||
use x86_64::structures::idt::InterruptStackFrame;
|
||||
use x86_64::structures::paging::OffsetPageTable;
|
||||
use x86_64::structures::paging::PhysFrame;
|
||||
use crate::{debug, print, println};
|
||||
use crate::memory::{BootInfoFrameAllocator, read_phys_memory32, write_phys_memory32};
|
||||
use crate::serial::{command, read};
|
||||
use crate::serial::simplifiers::handle_scancode;
|
||||
|
||||
// todo! maybe abstract this into different sections for different parts of cpu func?
|
||||
|
||||
pub const APIC_INTERRUPT_OFFSET: usize = 32;
|
||||
|
||||
pub const TIMER_IRQ: usize = 0 + APIC_INTERRUPT_OFFSET;
|
||||
pub const ERROR_IRQ: usize = 1 + APIC_INTERRUPT_OFFSET;
|
||||
pub const SPURIOUS_IRQ: usize = 2 + APIC_INTERRUPT_OFFSET;
|
||||
|
||||
pub const IOAPIC_IRQ_OFFSET: usize = 42;
|
||||
pub const FALLBACK_KEYBOARD_IRQ: usize = 1 + IOAPIC_IRQ_OFFSET;
|
||||
|
||||
|
||||
lazy_static!{
|
||||
static ref LAPIC: Mutex<LocalApic> = {
|
||||
// we need to get the xapic region
|
||||
let phys_addr = unsafe { xapic_base() };
|
||||
|
||||
let mut lapic = LocalApicBuilder::new()
|
||||
.timer_vector(TIMER_IRQ as usize)
|
||||
.spurious_vector(SPURIOUS_IRQ as usize)
|
||||
.error_vector(ERROR_IRQ as usize)
|
||||
.set_xapic_base(phys_addr)
|
||||
.build()
|
||||
.unwrap_or_else(|e| panic!("failed to build local apic: {}", e));
|
||||
Mutex::new(lapic)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn check_apic_compat() -> bool {
|
||||
unsafe {
|
||||
let mut eax: u32;
|
||||
|
@ -21,111 +54,54 @@ pub fn check_apic_compat() -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_apic_base(apic: usize) {
|
||||
const IA32_APIC_BASE_MSR_ENABLE: u32 = 0x800;
|
||||
let mut edx = 0u32;
|
||||
let mut eax = (apic & 0xfffff0000) as u32 | IA32_APIC_BASE_MSR_ENABLE;
|
||||
pub fn tell_pic8259a_to_f_off() {
|
||||
unsafe {
|
||||
asm!("wrmsr",
|
||||
in("ecx") 0x1b,
|
||||
in("eax") eax,
|
||||
in("edx") edx,
|
||||
);
|
||||
asm!("cli");
|
||||
asm!("out dx, al", in("dx") 0x20, in("al") 0x11i8);
|
||||
asm!("out dx, al", in("dx") 0xA0, in("al") 0x11i8);
|
||||
asm!("out dx, al", in("dx") 0x21, in("al") 0x20i8);
|
||||
asm!("out dx, al", in("dx") 0xA1, in("al") 0x28i8);
|
||||
asm!("out dx, al", in("dx") 0x21, in("al") 0x04i8);
|
||||
asm!("out dx, al", in("dx") 0xA1, in("al") 0x02i8);
|
||||
asm!("out dx, al", in("dx") 0x21, in("al") 0x01i8);
|
||||
asm!("out dx, al", in("dx") 0xA1, in("al") 0x01i8);
|
||||
asm!("out dx, al", in("dx") 0x21, in("al") 0x0i8);
|
||||
asm!("out dx, al", in("dx") 0xA1, in("al") 0x0i8);
|
||||
asm!("sti");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_apic_base() -> usize {
|
||||
let mut edx = 0u32;
|
||||
let mut eax = 0u32;
|
||||
unsafe {
|
||||
asm!("rdmsr",
|
||||
in("ecx") 0x1b,
|
||||
lateout("eax") eax,
|
||||
lateout("edx") edx,
|
||||
);
|
||||
}
|
||||
((edx as usize) << 32) | (eax as usize)
|
||||
}
|
||||
|
||||
pub fn enable_apic() {
|
||||
// PIC should be disabled by now
|
||||
// now enable local apic
|
||||
// 1. set bit 8 of spurious interrupt vector register
|
||||
let sivr_addr = 0xfee000f0;
|
||||
let sivr = read_phys_memory32(sivr_addr);
|
||||
write_phys_memory32(sivr_addr, sivr | (1 << 8));
|
||||
}
|
||||
|
||||
pub fn apic_read_io(ioapicaddr: u32, reg: u32) -> u32 {
|
||||
write_phys_memory32(ioapicaddr as u32, reg);
|
||||
read_phys_memory32(ioapicaddr as u32 + 0x10)
|
||||
}
|
||||
|
||||
pub fn apic_write_io(ioapicaddr: u32, reg: u32, val: u32) {
|
||||
write_phys_memory32(ioapicaddr as u32, reg);
|
||||
write_phys_memory32(ioapicaddr as u32 + 0x10, val);
|
||||
}
|
||||
|
||||
pub fn disable_pic() {
|
||||
command(0x20, 0x11);
|
||||
command(0xa0, 0x11);
|
||||
|
||||
command(0x21, 0xe0);
|
||||
command(0xa1, 0xe8);
|
||||
|
||||
command(0x21, 0x04);
|
||||
command(0xa1, 0x02);
|
||||
|
||||
command(0x21, 0x01);
|
||||
command(0xa1, 0x01);
|
||||
|
||||
command(0x21, 0xff);
|
||||
command(0xa1, 0xff);
|
||||
}
|
||||
|
||||
pub fn ioapic_set_irq(ioapicaddr: u32, irq: u8, apic_id: u64, vector:u8) {
|
||||
let lo_index: u32 = (0x10 + irq*2 ) as u32;
|
||||
let hi_index: u32 = (0x10 + irq*2 + 1) as u32;
|
||||
|
||||
let mut high = apic_read_io(ioapicaddr, hi_index);
|
||||
// set apic id
|
||||
high &= !(0xff000000);
|
||||
high |= (apic_id as u32) << 24;
|
||||
apic_write_io(ioapicaddr, hi_index, high);
|
||||
|
||||
let mut low = apic_read_io( ioapicaddr, lo_index);
|
||||
|
||||
// unmask
|
||||
low &= !(1 << 16);
|
||||
// set to physical delivery
|
||||
low &= !(1 << 11);
|
||||
// set to fixed delivery
|
||||
low &= !(0x700);
|
||||
// set vector
|
||||
low &= !(0xff);
|
||||
low |= vector as u32;
|
||||
|
||||
apic_write_io(ioapicaddr, lo_index, low);
|
||||
}
|
||||
|
||||
pub fn apic_eoi() {
|
||||
unsafe {
|
||||
asm!("mov eax, 0",
|
||||
"mov ecx, 0xb0",
|
||||
"wrmsr",
|
||||
in("ecx") 0x80b,
|
||||
in("eax") 0,
|
||||
);
|
||||
LAPIC.lock().enable();
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn timer(stack_frame: InterruptStackFrame) {
|
||||
end_of_interupt();
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn error(stack_frame: InterruptStackFrame) {
|
||||
println!("error interrupt");
|
||||
end_of_interupt();
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn spurious(stack_frame: InterruptStackFrame) {
|
||||
println!("spurious interrupt");
|
||||
end_of_interupt();
|
||||
}
|
||||
|
||||
fn end_of_interupt() {
|
||||
unsafe {
|
||||
LAPIC.lock().end_of_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
// todo! in the future this will be removed, it is only for testing basic apic functionality
|
||||
pub extern "x86-interrupt" fn keyboard_irq(stack_frame: InterruptStackFrame) {
|
||||
debug!("keyboard interrupt");
|
||||
unsafe { asm!("iretq"); }
|
||||
let scancode = read(0x60);
|
||||
|
||||
print!("ksc: {},", scancode);
|
||||
handle_scancode(scancode);
|
||||
|
||||
// reset keyboard controller
|
||||
let mut a = read(0x61);
|
||||
|
@ -133,10 +109,30 @@ pub extern "x86-interrupt" fn keyboard_irq(stack_frame: InterruptStackFrame) {
|
|||
command(0x61, a);
|
||||
a &= 0x7f;
|
||||
command(0x61, a);
|
||||
|
||||
end_of_interupt();
|
||||
}
|
||||
|
||||
// todo! we should abstract this away
|
||||
pub fn setup_apic_interrupts(ioapicaddr: u32) {
|
||||
// set keyboard irq to interrupt 40
|
||||
ioapic_set_irq(ioapicaddr, 1, 0, 40);
|
||||
pub fn setup_ioapic(ioapicaddr: u32, isos: Vec<InterruptSourceOverride>) {
|
||||
let mut ioapic = unsafe {
|
||||
IoApic::new(ioapicaddr as u64)
|
||||
};
|
||||
// setup keyboard interrupt
|
||||
unsafe {
|
||||
// FIXME! only for testing that this works, abstract ASAP!
|
||||
let gsi_keyboard = isos.iter().find(|iso| iso.isa_source == 1)
|
||||
.map(|iso| iso.global_system_interrupt).unwrap_or(1);
|
||||
// init with irq offset
|
||||
ioapic.init(IOAPIC_IRQ_OFFSET as u8);
|
||||
let mut entry = RedirectionTableEntry::default();
|
||||
entry.set_mode(IrqMode::Fixed);
|
||||
entry.set_flags(IrqFlags::LEVEL_TRIGGERED | IrqFlags::LOW_ACTIVE | IrqFlags::MASKED);
|
||||
entry.set_dest(0);
|
||||
entry.set_vector(gsi_keyboard as u8 + IOAPIC_IRQ_OFFSET as u8);
|
||||
ioapic.set_table_entry(1, entry);
|
||||
|
||||
ioapic.enable_irq(1);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,10 +14,11 @@ pub extern "x86-interrupt" fn breakpoint_exception(stack_frame: InterruptStackFr
|
|||
println!("stack frame: {:#?}", stack_frame);
|
||||
}
|
||||
|
||||
pub extern "x86-interrupt" fn double_fault(stack_frame: InterruptStackFrame, _error_code: u64) -> ! {
|
||||
pub extern "x86-interrupt" fn double_fault(stack_frame: InterruptStackFrame, error_code: u64) -> ! {
|
||||
println!("---KERNEL FUCKY WUKKY UWU---");
|
||||
println!("double fault!");
|
||||
println!("stack frame: {:#?}", stack_frame);
|
||||
println!("error code: {}", error_code);
|
||||
loop {}
|
||||
}
|
||||
|
||||
|
@ -28,4 +29,12 @@ pub extern "x86-interrupt" fn page_fault(stack_frame: InterruptStackFrame, error
|
|||
println!("error code: {:?}", error_code);
|
||||
println!("stack frame: {:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn unhandled(stack_frame: InterruptStackFrame, index: u8, error_code: Option<u64>) {
|
||||
println!("---KERNEL FUCKY WUKKY UWU---");
|
||||
println!("unhandled interrupt: {}", index);
|
||||
println!("error code: {:?}", error_code);
|
||||
println!("stack frame: {:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
219
src/lib.rs
219
src/lib.rs
|
@ -1,219 +0,0 @@
|
|||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(default_alloc_error_handler)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(asm_const)]
|
||||
#![feature(const_mut_refs)]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate rlibc;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec;
|
||||
use core::arch::asm;
|
||||
use lazy_static::lazy_static;
|
||||
use core::panic::PanicInfo;
|
||||
use multiboot2::MemoryAreaType;
|
||||
use spin::Mutex;
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||
use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use x86_64::{PhysAddr, VirtAddr};
|
||||
use x86_64::registers::segmentation::{CS, Segment, SS};
|
||||
use x86_64::structures::paging::Translate;
|
||||
use crate::boot::KernelInfo;
|
||||
use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::*;
|
||||
use crate::memory::{FRAME_ALLOC, MEM_MAPPER};
|
||||
use crate::serial::terminal::ST;
|
||||
|
||||
mod font;
|
||||
mod serial;
|
||||
mod internals;
|
||||
mod security;
|
||||
mod boot;
|
||||
mod memory;
|
||||
mod macros;
|
||||
|
||||
lazy_static! {
|
||||
static ref GDT: Mutex<GlobalDescriptorTable> = {
|
||||
let mut gdt = GlobalDescriptorTable::new();
|
||||
Mutex::new(gdt)
|
||||
};
|
||||
static ref IDT: InterruptDescriptorTable = {
|
||||
let mut idt = InterruptDescriptorTable::new();
|
||||
idt.breakpoint.set_handler_fn(internals::errors::breakpoint_exception);
|
||||
idt.double_fault.set_handler_fn(internals::errors::double_fault);
|
||||
idt.page_fault.set_handler_fn(internals::errors::page_fault);
|
||||
idt[40].set_handler_fn(internals::cpu::keyboard_irq);
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const RAINBOW : [Colour; 6] = [Colour{r:255,g:0,b:0}, Colour{r:255,g:127,b:0}, Colour{r:255,g:255,b:0}, Colour{r:0,g:255,b:0}, Colour{r:0,g:255,b:255}, Colour{r:0,g:0,b:255}];
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||
panic!("allocation error: {:?}", layout)
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
println!("---KERNEL FUCKY WUKKY UWU (panic)---");
|
||||
if let Some(s) = info.payload().downcast_ref::<&str>() {
|
||||
println!("panic payload: {s:?}")
|
||||
} else {
|
||||
println!("no panic payload")
|
||||
};
|
||||
if let Some(msg) = info.message() {
|
||||
println!("panic msg: {}", msg.as_str().unwrap_or("no message"))
|
||||
} else {
|
||||
println!("no message");
|
||||
}
|
||||
if let Some(location) = info.location() {
|
||||
println!("location: file {} line {}", location.file(), location.line());
|
||||
} else {
|
||||
println!("no location");
|
||||
};
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KernelArgs {
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
multiboot_information_address: usize
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn kernel_main(args: KernelArgs) -> ! {
|
||||
// initialise serial
|
||||
let mut serial_ports = serial::init_serial();
|
||||
let mut console_port = None;
|
||||
for (i, enabled) in serial_ports.ports_enabled.iter().enumerate() {
|
||||
if *enabled {
|
||||
console_port = Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i) = console_port {
|
||||
let port = &serial_ports.ports[i];
|
||||
ST.init_from_port(*port);
|
||||
println!("using serial port {} as console", i);
|
||||
}
|
||||
|
||||
// temporarily disable interrupts
|
||||
x86_64::instructions::interrupts::disable();
|
||||
println!("debug: setup GDT");
|
||||
// load TSS
|
||||
static mut tss: TaskStateSegment = TaskStateSegment::new();
|
||||
{
|
||||
unsafe {
|
||||
tss.interrupt_stack_table[0] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
// set word at offset 102 to 0x68 and last two bytes of the tss to 0xffff
|
||||
// this is a hack to make the tss valid
|
||||
let tss_ptr = &tss as *const TaskStateSegment as *mut u8;
|
||||
unsafe {
|
||||
*tss_ptr.add(102) = 0x68;
|
||||
*tss_ptr.add(104) = 0xff;
|
||||
*tss_ptr.add(105) = 0xff;
|
||||
}
|
||||
}
|
||||
let kcs = GDT.lock().add_entry(Descriptor::kernel_code_segment());
|
||||
let kds = GDT.lock().add_entry(Descriptor::kernel_data_segment());
|
||||
let tsss = unsafe { GDT.lock().add_entry(Descriptor::tss_segment(&tss)) };
|
||||
// load GDT
|
||||
unsafe {
|
||||
GDT.lock().load_unsafe();
|
||||
}
|
||||
println!("debug: GDT loaded");
|
||||
// set code segment to kernel code segment
|
||||
unsafe {
|
||||
CS::set_reg(kcs);
|
||||
}
|
||||
println!("debug: CS set");
|
||||
// set data segment to kernel data segment
|
||||
unsafe {
|
||||
SS::set_reg(kds);
|
||||
}
|
||||
println!("debug: SS set");
|
||||
// load TSS
|
||||
unsafe {
|
||||
x86_64::instructions::tables::load_tss(tsss);
|
||||
}
|
||||
println!("debug: TSS loaded");
|
||||
|
||||
// load IDT
|
||||
IDT.load();
|
||||
println!("debug: IDT loaded");
|
||||
// enable interrupts
|
||||
x86_64::instructions::interrupts::enable();
|
||||
}
|
||||
|
||||
|
||||
println!();
|
||||
println!();
|
||||
println!();
|
||||
println!("welcome to wukkOS!");
|
||||
println!("(c) 2022 Real Microsoft, LLC");
|
||||
let kern_info = Mutex::new(KernelInfo::init_from_kernel_args(args));
|
||||
|
||||
// memory stuff
|
||||
{
|
||||
print!("initialising mapper...");
|
||||
MEM_MAPPER.lock().replace(unsafe { memory::init(VirtAddr::new(0)) });
|
||||
println!("[OK]");
|
||||
print!("initialising frame allocator...");
|
||||
FRAME_ALLOC.lock().replace(unsafe { memory::BootInfoFrameAllocator::init(kern_info) });
|
||||
println!("[OK]");
|
||||
print!("initialising heap...");
|
||||
memory::allocator::init_heap(MEM_MAPPER.lock().as_mut().unwrap(), FRAME_ALLOC.lock().as_mut().unwrap()).expect("heap init failed");
|
||||
println!("[OK]");
|
||||
|
||||
print!("testing heap...");
|
||||
let reference_counted = Rc::new(vec![1, 2, 3]);
|
||||
let cloned = reference_counted.clone();
|
||||
let test_1 = Rc::strong_count(&reference_counted) == 2;
|
||||
drop(cloned);
|
||||
let test_2 = Rc::strong_count(&reference_counted) == 1;
|
||||
if test_1 && test_2 {
|
||||
println!("[OK]");
|
||||
} else {
|
||||
println!("[FAIL]");
|
||||
}
|
||||
drop(reference_counted);
|
||||
}
|
||||
|
||||
// apic stuff
|
||||
{
|
||||
print!("checking for apic compatibility...");
|
||||
let apic_compatible = unsafe { internals::cpu::check_apic_compat() };
|
||||
if apic_compatible {
|
||||
println!("[OK]");
|
||||
} else {
|
||||
println!("[FAIL]");
|
||||
panic!("apic required at the moment");
|
||||
}
|
||||
print!("disabling PIC...");
|
||||
unsafe { internals::cpu::disable_pic() };
|
||||
println!("[OK]");
|
||||
print!("initialising apic...");
|
||||
unsafe { internals::cpu::enable_apic() };
|
||||
println!("[OK]");
|
||||
print!("setting up apic interrupts...");
|
||||
unsafe { internals::cpu::setup_apic_interrupts(kern_info.lock().acpi_get_ioapic_addr()) };
|
||||
println!("[OK]");
|
||||
// enable interrupts
|
||||
x86_64::instructions::interrupts::enable();
|
||||
}
|
||||
|
||||
loop {
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
use core::fmt;
|
||||
use limine::LimineTerminalResponse;
|
||||
use crate::boot::LimineWriter;
|
||||
use crate::serial::terminal::ST;
|
||||
|
||||
#[macro_export]
|
||||
|
@ -21,6 +23,9 @@ macro_rules! debug {
|
|||
pub fn _print(args: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
ST.writer.lock().write_fmt(args).unwrap();
|
||||
|
||||
let mut limine_writer = LimineWriter;
|
||||
limine_writer.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -31,5 +36,10 @@ pub fn _debug(args: fmt::Arguments) {
|
|||
ST.log("[debug] ");
|
||||
ST.writer.lock().write_fmt(args).unwrap();
|
||||
ST.logln("");
|
||||
|
||||
let mut limine_writer = LimineWriter;
|
||||
limine_writer.write_str("[debug] ").unwrap();
|
||||
limine_writer.write_fmt(args).unwrap();
|
||||
limine_writer.write_str("\n").unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(default_alloc_error_handler)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(asm_const)]
|
||||
#![feature(const_mut_refs)]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(const_slice_from_raw_parts_mut)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate rlibc;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec;
|
||||
use core::arch::asm;
|
||||
use lazy_static::lazy_static;
|
||||
use core::panic::PanicInfo;
|
||||
use limine::{LimineBootInfoRequest, LimineMemmapRequest, LimineTerminalRequest};
|
||||
use spin::Mutex;
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||
use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use x86_64::{PhysAddr, set_general_handler, VirtAddr};
|
||||
use x86_64::registers::segmentation::{CS, Segment, SS};
|
||||
use x86_64::structures::paging::Translate;
|
||||
use crate::boot::{get_ioapic_info, KERNEL_ADDRESS};
|
||||
use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::*;
|
||||
use crate::memory::{FRAME_ALLOC, MEM_MAPPER};
|
||||
use crate::serial::terminal::ST;
|
||||
|
||||
mod font;
|
||||
mod serial;
|
||||
mod internals;
|
||||
mod security;
|
||||
mod boot;
|
||||
mod memory;
|
||||
mod macros;
|
||||
|
||||
lazy_static! {
|
||||
//pub static ref KERN_INFO: Mutex<Option<KernelInfo>> = Mutex::new(None);
|
||||
static ref GDT: Mutex<GlobalDescriptorTable> = {
|
||||
let mut gdt = GlobalDescriptorTable::new();
|
||||
Mutex::new(gdt)
|
||||
};
|
||||
static ref IDT: InterruptDescriptorTable = {
|
||||
let mut idt = InterruptDescriptorTable::new();
|
||||
unsafe {
|
||||
use internals::errors::unhandled;
|
||||
set_general_handler!(&mut idt, unhandled);
|
||||
idt.breakpoint.set_handler_fn(internals::errors::breakpoint_exception).set_stack_index(0);
|
||||
idt.double_fault.set_handler_fn(internals::errors::double_fault).set_stack_index(0);
|
||||
idt.page_fault.set_handler_fn(internals::errors::page_fault).set_stack_index(0);
|
||||
idt[internals::cpu::TIMER_IRQ].set_handler_fn(internals::cpu::timer).set_stack_index(1);
|
||||
idt[internals::cpu::ERROR_IRQ].set_handler_fn(internals::cpu::error).set_stack_index(1);
|
||||
idt[internals::cpu::SPURIOUS_IRQ].set_handler_fn(internals::cpu::spurious).set_stack_index(1);
|
||||
idt[internals::cpu::FALLBACK_KEYBOARD_IRQ].set_handler_fn(internals::cpu::keyboard_irq).set_stack_index(1);
|
||||
}
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const RAINBOW : [Colour; 6] = [Colour{r:255,g:0,b:0}, Colour{r:255,g:127,b:0}, Colour{r:255,g:255,b:0}, Colour{r:0,g:255,b:0}, Colour{r:0,g:255,b:255}, Colour{r:0,g:0,b:255}];
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||
panic!("allocation error: {:?}", layout)
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
println!("---KERNEL FUCKY WUKKY UWU (panic)---");
|
||||
if let Some(s) = info.payload().downcast_ref::<&str>() {
|
||||
println!("panic payload: {s:?}")
|
||||
} else {
|
||||
println!("no panic payload")
|
||||
};
|
||||
if let Some(msg) = info.message() {
|
||||
println!("panic msg: {}", msg)
|
||||
} else {
|
||||
println!("no message");
|
||||
}
|
||||
if let Some(location) = info.location() {
|
||||
println!("location: file {} line {}", location.file(), location.line());
|
||||
} else {
|
||||
println!("no location");
|
||||
};
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn kernel_main() -> ! {
|
||||
debug!("entry point");
|
||||
|
||||
// initialise serial
|
||||
let mut serial_ports = serial::init_serial();
|
||||
let mut console_port = None;
|
||||
for (i, enabled) in serial_ports.ports_enabled.iter().enumerate() {
|
||||
if *enabled {
|
||||
console_port = Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i) = console_port {
|
||||
let port = &serial_ports.ports[i];
|
||||
ST.init_from_port(*port);
|
||||
println!("using serial port {} as console", i);
|
||||
}
|
||||
|
||||
// temporarily disable interrupts
|
||||
x86_64::instructions::interrupts::disable();
|
||||
println!("debug: setup GDT");
|
||||
// load TSS
|
||||
static mut tss: TaskStateSegment = TaskStateSegment::new();
|
||||
{
|
||||
unsafe {
|
||||
{
|
||||
tss.interrupt_stack_table[0] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.interrupt_stack_table[1] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.interrupt_stack_table[2] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.interrupt_stack_table[3] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.interrupt_stack_table[4] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.interrupt_stack_table[5] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.interrupt_stack_table[6] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
}
|
||||
{
|
||||
tss.privilege_stack_table[0] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.privilege_stack_table[1] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
tss.privilege_stack_table[2] = {
|
||||
const STACK_SIZE: usize = 4096 * 5;
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
||||
let stack_end = stack_start + STACK_SIZE;
|
||||
stack_end
|
||||
};
|
||||
}
|
||||
// set word at offset 102 to 0x68 and last two bytes of the tss to 0xffff
|
||||
// this is a hack to make the tss valid
|
||||
let tss_ptr = &tss as *const TaskStateSegment as *mut u8;
|
||||
unsafe {
|
||||
*tss_ptr.add(102) = 0x68;
|
||||
*tss_ptr.add(104) = 0xff;
|
||||
*tss_ptr.add(105) = 0xff;
|
||||
}
|
||||
}
|
||||
let kcs = GDT.lock().add_entry(Descriptor::kernel_code_segment());
|
||||
let kds = GDT.lock().add_entry(Descriptor::kernel_data_segment());
|
||||
let tsss = unsafe { GDT.lock().add_entry(Descriptor::tss_segment(&tss)) };
|
||||
// load GDT
|
||||
unsafe {
|
||||
GDT.lock().load_unsafe();
|
||||
}
|
||||
println!("debug: GDT loaded");
|
||||
// set code segment to kernel code segment
|
||||
unsafe {
|
||||
CS::set_reg(kcs);
|
||||
}
|
||||
println!("debug: CS set");
|
||||
// set data segment to kernel data segment
|
||||
unsafe {
|
||||
SS::set_reg(kds);
|
||||
}
|
||||
println!("debug: SS set");
|
||||
// load TSS
|
||||
unsafe {
|
||||
x86_64::instructions::tables::load_tss(tsss);
|
||||
}
|
||||
println!("debug: TSS loaded");
|
||||
|
||||
// load IDT
|
||||
IDT.load();
|
||||
println!("debug: IDT loaded");
|
||||
// enable interrupts
|
||||
x86_64::instructions::interrupts::enable();
|
||||
}
|
||||
|
||||
|
||||
println!();
|
||||
println!();
|
||||
println!();
|
||||
println!("welcome to wukkOS!");
|
||||
println!("(c) 2022 Real Microsoft, LLC");
|
||||
|
||||
// memory stuff
|
||||
{
|
||||
print!("initialising mapper...");
|
||||
let kernel_physical_address = KERNEL_ADDRESS.get_response().get().unwrap().physical_base;
|
||||
let kernel_virtual_address = KERNEL_ADDRESS.get_response().get().unwrap().virtual_base;
|
||||
debug!("kernel physical address: {:#x}", kernel_physical_address);
|
||||
debug!("kernel virtual address: {:#x}", kernel_virtual_address);
|
||||
let offset = (kernel_virtual_address as i64) as usize;// - kernel_physical_address as i64) as usize;
|
||||
MEM_MAPPER.lock().replace(unsafe { memory::init(VirtAddr::new(0)) });
|
||||
println!("[OK]");
|
||||
print!("initialising frame allocator...");
|
||||
FRAME_ALLOC.lock().replace(unsafe { memory::BootInfoFrameAllocator::init() });
|
||||
println!("[OK]");
|
||||
print!("initialising heap...");
|
||||
memory::allocator::init_heap(MEM_MAPPER.lock().as_mut().unwrap(), FRAME_ALLOC.lock().as_mut().unwrap()).expect("heap init failed");
|
||||
println!("[OK]");
|
||||
|
||||
print!("testing heap...");
|
||||
let reference_counted = Rc::new(vec![1, 2, 3]);
|
||||
let cloned = reference_counted.clone();
|
||||
let test_1 = Rc::strong_count(&reference_counted) == 2;
|
||||
drop(cloned);
|
||||
let test_2 = Rc::strong_count(&reference_counted) == 1;
|
||||
if test_1 && test_2 {
|
||||
println!("[OK]");
|
||||
} else {
|
||||
println!("[FAIL]");
|
||||
}
|
||||
drop(reference_counted);
|
||||
}
|
||||
|
||||
// apic stuff
|
||||
{
|
||||
print!("checking for apic compatibility...");
|
||||
let apic_compatible = unsafe { internals::cpu::check_apic_compat() };
|
||||
if apic_compatible {
|
||||
println!("[OK]");
|
||||
} else {
|
||||
println!("[FAIL]");
|
||||
panic!("apic required at the moment");
|
||||
}
|
||||
print!("initialising apic...");
|
||||
//internals::cpu::tell_pic8259a_to_f_off();
|
||||
let (addr, isos) = get_ioapic_info();
|
||||
unsafe { internals::cpu::enable_apic() };
|
||||
println!("[OK]");
|
||||
print!("setting up apic interrupts...");
|
||||
debug!("ioapicaddr: {:#x}", addr);
|
||||
unsafe { internals::cpu::setup_ioapic(addr, isos) };
|
||||
println!("[OK]");
|
||||
// enable interrupts
|
||||
//x86_64::instructions::interrupts::enable();
|
||||
}
|
||||
|
||||
loop {
|
||||
x86_64::instructions::hlt();
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ use core::ptr::NonNull;
|
|||
use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB};
|
||||
use x86_64::structures::paging::mapper::MapToError;
|
||||
use x86_64::VirtAddr;
|
||||
use crate::memory::PageSize;
|
||||
use super::Locked;
|
||||
|
||||
pub const HEAP_START: u64 = 0x_4444_4444_0000;
|
||||
|
@ -99,9 +100,9 @@ fn list_index(layout: &core::alloc::Layout) -> Option<usize> {
|
|||
}
|
||||
|
||||
pub fn init_heap(
|
||||
mapper: &mut impl Mapper<Size4KiB>,
|
||||
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
||||
) -> Result<(), MapToError<Size4KiB>> {
|
||||
mapper: &mut impl Mapper<PageSize>,
|
||||
frame_allocator: &mut impl FrameAllocator<PageSize>,
|
||||
) -> Result<(), MapToError<PageSize>> {
|
||||
let page_range = {
|
||||
let heap_start = VirtAddr::new(HEAP_START as u64);
|
||||
let heap_end = heap_start + HEAP_SIZE - 1u64;
|
||||
|
|
|
@ -3,14 +3,19 @@ pub mod allocator;
|
|||
use alloc::boxed::Box;
|
||||
use alloc::sync::Arc;
|
||||
use lazy_static::lazy_static;
|
||||
use x86_64::structures::paging::{FrameAllocator, Mapper, OffsetPageTable, PageTable, PhysFrame, Size4KiB, Translate};
|
||||
use limine::LimineMemoryMapEntryType;
|
||||
use x86_64::structures::paging::{FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PageTableFlags, PhysFrame, Size4KiB, Translate};
|
||||
use x86_64::{PhysAddr, VirtAddr};
|
||||
|
||||
lazy_static!{
|
||||
pub static ref MEM_MAPPER: Mutex<Option<OffsetPageTable>> = Mutex::new(None);
|
||||
pub static ref MEM_MAPPER: Mutex<Option<OffsetPageTable<'static>>> = Mutex::new(None);
|
||||
pub static ref FRAME_ALLOC: Mutex<Option<BootInfoFrameAllocator>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
pub const VIRT_MEM_OFFSET: u64 = 0xffffffff80000000;
|
||||
|
||||
pub type PageSize = Size4KiB;
|
||||
|
||||
pub struct Locked<A> {
|
||||
inner: spin::Mutex<A>,
|
||||
}
|
||||
|
@ -44,22 +49,17 @@ unsafe fn active_level_4_table(phys_mem_offset: VirtAddr) -> &'static mut PageTa
|
|||
unsafe { &mut *page_table_ptr } // unsafe
|
||||
}
|
||||
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
use multiboot2::{MemoryMapTag, BootInformation};
|
||||
use spin::Mutex;
|
||||
use crate::boot::KernelInfo;
|
||||
use crate::{debug, print, println};
|
||||
use crate::boot::{KERNEL_ADDRESS, MEM_MAP};
|
||||
|
||||
pub struct BootInfoFrameAllocator {
|
||||
kern_info: Mutex<KernelInfo>,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl BootInfoFrameAllocator {
|
||||
#[cfg(feature = "f_multiboot2")]
|
||||
pub unsafe fn init(kern_info: Mutex<crate::boot::KernelInfo>) -> Self {
|
||||
pub unsafe fn init() -> Self {
|
||||
Self {
|
||||
kern_info,
|
||||
next: 0,
|
||||
}
|
||||
}
|
||||
|
@ -67,14 +67,14 @@ impl BootInfoFrameAllocator {
|
|||
|
||||
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<PhysFrame> {
|
||||
#[cfg(feature = "f_multiboot2")] {
|
||||
let mut kern_lock = self.kern_info.lock();
|
||||
let mut usable_frames = kern_lock
|
||||
.memory_areas();
|
||||
let mut usable_frames = usable_frames
|
||||
#[cfg(feature = "f_limine")] {
|
||||
let mmap = MEM_MAP.get_response().get().expect("failed to get memory map")
|
||||
.memmap();
|
||||
let mut usable_frames = mmap.iter()
|
||||
.filter(|entry| entry.typ == LimineMemoryMapEntryType::Usable)
|
||||
.map(|area| {
|
||||
let frame_addr = area.start_address();
|
||||
let frame_end = area.end_address();
|
||||
let frame_addr = area.base;
|
||||
let frame_end = area.base + area.len;
|
||||
let frame_size = frame_end - frame_addr;
|
||||
let num_frames = frame_size / 4096;
|
||||
let start_frame = PhysFrame::containing_address(PhysAddr::new(frame_addr));
|
||||
|
@ -84,9 +84,6 @@ unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
|||
let frame = usable_frames.nth(self.next).clone();
|
||||
self.next += 1;
|
||||
|
||||
// ensure unlock
|
||||
unsafe { self.kern_info.force_unlock() };
|
||||
|
||||
frame
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use core::ops::Deref;
|
|||
|
||||
pub mod terminal_helpers;
|
||||
pub mod terminal;
|
||||
pub mod simplifiers;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum potential_serial_ports {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
use lazy_static::lazy_static;
|
||||
use pc_keyboard::{Keyboard, layouts, ScancodeSet1, HandleControl};
|
||||
use pc_keyboard::DecodedKey::Unicode;
|
||||
use spin::Mutex;
|
||||
use crate::print;
|
||||
|
||||
lazy_static!{
|
||||
static ref KBD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(Keyboard::new(HandleControl::MapLettersToUnicode));
|
||||
}
|
||||
|
||||
pub fn handle_scancode(scancode: u8) {
|
||||
let mut kbd = KBD.lock();
|
||||
if let Ok(Some(key_event)) = kbd.add_byte(scancode) {
|
||||
if let Some(key) = kbd.process_keyevent(key_event) {
|
||||
if let Unicode(c) = key {
|
||||
print!("{}", c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,14 @@
|
|||
"executables": true,
|
||||
"linker-flavor": "ld.lld",
|
||||
"linker": "rust-lld",
|
||||
"panic-strategy": "abort",
|
||||
"disable-redzone": true,
|
||||
"features": "-mmx,-sse,+soft-float"
|
||||
"panic-strategy": "abort",
|
||||
"features": "-mmx,-sse,+soft-float",
|
||||
"code-model": "kernel",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"--gc-sections",
|
||||
"--script=arch/x86_64/linker.ld"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue