Compare commits

...

5 Commits

Author SHA1 Message Date
fekhesk 314189882b
ITS FUCKING WORKING OH MY GOD 2022-10-27 21:58:36 -07:00
fekhesk d11d5ddd97
wtf double fault ): 2022-10-27 20:08:32 -07:00
fekhesk 8af40d0414
some of the stuff now works with limine 2022-10-27 18:06:33 -07:00
fekhesk 600ba7163a
redoing apic stuff 2022-10-27 15:40:21 -07:00
fekhesk 8fcc70bee1
make it boot on my macbook 2022-10-27 14:29:40 -07:00
24 changed files with 618 additions and 707 deletions

1
.gitignore vendored
View File

@ -37,3 +37,4 @@ Cargo.lock
/boot.o
/build/
/OVMF-pure-efi.fd
/isodir

View File

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

View File

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

View File

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

View File

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

View File

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

5
arch/x86_64/limine.cfg Normal file
View File

@ -0,0 +1,5 @@
TIMEOUT=0
:wukkOS
PROTOCOL=limine
KERNEL_PATH=boot:///boot/wukkOS.bin

View File

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

View File

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

View File

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

5
byob/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# limine binaries
limine.sys
limine-cd.bin
limine-cd-efi.bin
limine-deploy

9
byob/README.md Normal file
View File

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

View File

@ -1,12 +1,6 @@
[=3h[=3h[=3h[=3hBdsDxe: 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)
Welcome to GRUB!
 Booting `wukkOS basic kernel'
WARNING: no console will be available to OS
error: no suitable video mode found.
using serial port 0 as console
using 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

View File

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

View File

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

View File

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

View File

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

View File

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

296
src/main.rs Normal file
View File

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

View File

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

View File

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

View File

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

20
src/serial/simplifiers.rs Normal file
View File

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

View File

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