From 4b25a4db89f66a9b144e3a03205f1a1cf63627fc Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Thu, 10 Oct 2024 18:06:54 +0300 Subject: [PATCH] i686: add platform support --- Cargo.lock | 16 + etc/i686-unknown-none.json | 29 ++ etc/i686-unknown-none.ld | 53 +++ kernel/Cargo.toml | 4 + kernel/arch/Cargo.toml | 3 + kernel/arch/i686/Cargo.toml | 16 + kernel/arch/i686/src/context.S | 136 ++++++ kernel/arch/i686/src/context.rs | 395 ++++++++++++++++++ kernel/arch/i686/src/gdt.rs | 71 ++++ kernel/arch/i686/src/lib.rs | 113 +++++ kernel/arch/i686/src/mem/fixed.rs | 87 ++++ kernel/arch/i686/src/mem/mod.rs | 79 ++++ kernel/arch/i686/src/mem/process.rs | 125 ++++++ kernel/arch/i686/src/mem/table.rs | 210 ++++++++++ kernel/arch/i686/src/registers.rs | 156 +++++++ kernel/arch/interface/src/lib.rs | 4 + kernel/arch/interface/src/mem/mod.rs | 3 + kernel/arch/interface/src/task.rs | 2 +- kernel/arch/src/lib.rs | 2 + kernel/build.rs | 26 +- kernel/driver/bus/pci/src/capability.rs | 5 +- kernel/driver/bus/pci/src/lib.rs | 34 +- kernel/driver/bus/pci/src/space/legacy.rs | 46 ++ kernel/driver/bus/pci/src/space/mod.rs | 12 +- kernel/driver/input/src/lib.rs | 5 +- kernel/driver/usb/xhci/src/lib.rs | 11 +- kernel/lib/vmalloc/src/lib.rs | 4 +- kernel/libk/libk-mm/interface/src/address.rs | 15 + kernel/libk/libk-mm/interface/src/pointer.rs | 12 + kernel/libk/libk-mm/interface/src/table.rs | 7 +- kernel/libk/libk-mm/src/lib.rs | 4 + kernel/libk/libk-mm/src/phys/manager.rs | 18 +- kernel/libk/libk-mm/src/phys/mod.rs | 14 +- kernel/libk/src/module.rs | 4 +- kernel/libk/src/task/binary/elf.rs | 19 +- kernel/libk/src/task/binary/mod.rs | 13 +- kernel/libk/src/task/mem.rs | 4 + kernel/libk/src/task/types.rs | 11 +- kernel/libk/src/vfs/terminal.rs | 49 ++- kernel/modules/test_mod/Cargo.lock | 15 + kernel/src/arch/i686/boot/mod.rs | 148 +++++++ kernel/src/arch/i686/boot/multiboot.S | 56 +++ kernel/src/arch/i686/exception.rs | 272 ++++++++++++ kernel/src/arch/i686/mod.rs | 248 +++++++++++ kernel/src/arch/i686/peripherals/mod.rs | 1 + kernel/src/arch/i686/peripherals/textfb.rs | 152 +++++++ kernel/src/arch/i686/vectors.S | 173 ++++++++ kernel/src/arch/mod.rs | 10 +- kernel/src/arch/x86/gdt.rs | 313 ++++++++++++++ kernel/src/arch/{x86_64 => x86}/intrinsics.rs | 9 +- kernel/src/arch/x86/mod.rs | 13 + .../arch/{x86_64 => x86}/peripherals/i8253.rs | 27 +- kernel/src/arch/x86/peripherals/i8259.rs | 206 +++++++++ .../src/arch/x86/peripherals/i8259_vectors.S | 85 ++++ .../arch/{x86_64 => x86}/peripherals/mod.rs | 3 +- .../peripherals/ps2/codeset.rs | 0 .../{x86_64 => x86}/peripherals/ps2/mod.rs | 13 +- .../{x86_64 => x86}/peripherals/serial.rs | 2 +- kernel/src/arch/x86_64/gdt.rs | 199 --------- kernel/src/arch/x86_64/mod.rs | 51 +-- kernel/src/debug.rs | 2 +- kernel/src/device/display/console.rs | 21 +- kernel/src/init.rs | 3 +- kernel/src/main.rs | 1 + kernel/src/syscall/imp/sys_io.rs | 6 +- kernel/src/syscall/mod.rs | 4 +- kernel/src/task/mod.rs | 4 +- kernel/src/util/mod.rs | 4 + kernel/tools/gentables/src/main.rs | 16 + lib/abi/build.rs | 24 +- lib/abi/def/io.abi | 2 +- lib/abi/src/arch/i686.rs | 18 + lib/abi/src/arch/mod.rs | 5 + lib/qemu/src/i386.rs | 88 ++++ lib/qemu/src/lib.rs | 24 +- lib/runtime/build.rs | 24 +- lib/runtime/src/lib.rs | 1 + lib/runtime/src/sys/i686.rs | 95 +++++ lib/runtime/src/sys/mod.rs | 3 + tool/abi-generator/src/abi/mod.rs | 19 +- tool/abi-generator/src/abi/ty/mod.rs | 2 + tool/abi-generator/src/abi/ty/primitive.rs | 11 +- userspace/Cargo.lock | 174 -------- userspace/Cargo.toml | 4 +- userspace/arch/i686/inittab | 4 + userspace/init/src/main.rs | 1 + userspace/sysutils/Cargo.toml | 8 +- xtask/src/build/i686.rs | 36 ++ xtask/src/build/mod.rs | 16 +- xtask/src/build/userspace.rs | 34 +- xtask/src/check.rs | 5 + xtask/src/env.rs | 4 + xtask/src/main.rs | 2 +- xtask/src/qemu.rs | 46 +- 94 files changed, 3979 insertions(+), 545 deletions(-) create mode 100644 etc/i686-unknown-none.json create mode 100644 etc/i686-unknown-none.ld create mode 100644 kernel/arch/i686/Cargo.toml create mode 100644 kernel/arch/i686/src/context.S create mode 100644 kernel/arch/i686/src/context.rs create mode 100644 kernel/arch/i686/src/gdt.rs create mode 100644 kernel/arch/i686/src/lib.rs create mode 100644 kernel/arch/i686/src/mem/fixed.rs create mode 100644 kernel/arch/i686/src/mem/mod.rs create mode 100644 kernel/arch/i686/src/mem/process.rs create mode 100644 kernel/arch/i686/src/mem/table.rs create mode 100644 kernel/arch/i686/src/registers.rs create mode 100644 kernel/driver/bus/pci/src/space/legacy.rs create mode 100644 kernel/src/arch/i686/boot/mod.rs create mode 100644 kernel/src/arch/i686/boot/multiboot.S create mode 100644 kernel/src/arch/i686/exception.rs create mode 100644 kernel/src/arch/i686/mod.rs create mode 100644 kernel/src/arch/i686/peripherals/mod.rs create mode 100644 kernel/src/arch/i686/peripherals/textfb.rs create mode 100644 kernel/src/arch/i686/vectors.S create mode 100644 kernel/src/arch/x86/gdt.rs rename kernel/src/arch/{x86_64 => x86}/intrinsics.rs (95%) create mode 100644 kernel/src/arch/x86/mod.rs rename kernel/src/arch/{x86_64 => x86}/peripherals/i8253.rs (77%) create mode 100644 kernel/src/arch/x86/peripherals/i8259.rs create mode 100644 kernel/src/arch/x86/peripherals/i8259_vectors.S rename kernel/src/arch/{x86_64 => x86}/peripherals/mod.rs (52%) rename kernel/src/arch/{x86_64 => x86}/peripherals/ps2/codeset.rs (100%) rename kernel/src/arch/{x86_64 => x86}/peripherals/ps2/mod.rs (91%) rename kernel/src/arch/{x86_64 => x86}/peripherals/serial.rs (96%) delete mode 100644 kernel/src/arch/x86_64/gdt.rs create mode 100644 lib/abi/src/arch/i686.rs create mode 100644 lib/qemu/src/i386.rs create mode 100644 lib/runtime/src/sys/i686.rs create mode 100644 userspace/arch/i686/inittab create mode 100644 xtask/src/build/i686.rs diff --git a/Cargo.lock b/Cargo.lock index 5ee35c20..69f64363 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -827,6 +827,7 @@ dependencies = [ "cfg-if", "kernel-arch-aarch64", "kernel-arch-hosted", + "kernel-arch-i686", "kernel-arch-interface", "kernel-arch-x86_64", ] @@ -855,6 +856,20 @@ dependencies = [ "yggdrasil-abi", ] +[[package]] +name = "kernel-arch-i686" +version = "0.1.0" +dependencies = [ + "bitflags 2.6.0", + "device-api", + "kernel-arch-interface", + "libk-mm-interface", + "log", + "static_assertions", + "tock-registers", + "yggdrasil-abi", +] + [[package]] name = "kernel-arch-interface" version = "0.1.0" @@ -2167,6 +2182,7 @@ dependencies = [ "git-version", "kernel-arch", "kernel-arch-aarch64", + "kernel-arch-i686", "kernel-arch-x86_64", "kernel-fs", "libk", diff --git a/etc/i686-unknown-none.json b/etc/i686-unknown-none.json new file mode 100644 index 00000000..03ec0352 --- /dev/null +++ b/etc/i686-unknown-none.json @@ -0,0 +1,29 @@ +{ + "is-builtin": false, + "arch": "x86", + "cpu": "pentium4", + "os": "none", + "llvm-target": "i686-unknown-linux-gnu", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + "max-atomic-width": 64, + "target-pointer-width": "32", + "features": "-avx,-sse,+soft-float", + + "executables": true, + "stack-probes": { + "kind": "inline" + }, + "dynamic-linking": true, + "panic-strategy": "abort", + "relocation-model": "pic", + + "has-thread-local": false, + + "supported-split-debuginfo": [ + "packed", + "unpacked", + "off" + ], + "linker": "rust-lld", + "linker-flavor": "ld.lld" +} diff --git a/etc/i686-unknown-none.ld b/etc/i686-unknown-none.ld new file mode 100644 index 00000000..36e6202c --- /dev/null +++ b/etc/i686-unknown-none.ld @@ -0,0 +1,53 @@ +ENTRY(__i686_entry); + +KERNEL_PHYS_BASE = 0x100000; +KERNEL_VIRT_OFFSET = 0xC0000000; + +SECTIONS { + . = KERNEL_PHYS_BASE; + PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET); + + .text.entry : { + KEEP(*(.multiboot)) + *(.text.entry) + } + + . = ALIGN(16); + . = . + KERNEL_VIRT_OFFSET; + + .text : AT(. - KERNEL_VIRT_OFFSET) { + *(.text*) + } + + .export.text : AT(. - KERNEL_VIRT_OFFSET) { + KEEP(*(.export.text*)) + } + + . = ALIGN(4K); + .rodata : AT(. - KERNEL_VIRT_OFFSET) { + *(.eh_frame*) + *(.rodata*) + } + + . = ALIGN(4K); + .data.tables : AT (. - KERNEL_VIRT_OFFSET) { + KEEP(*(.data.tables)) + } + + .data : AT(. - KERNEL_VIRT_OFFSET) { + KEEP(*(.data.yboot)) + *(.data*) + *(.got*) + } + + . = ALIGN(4K); + PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET); + .bss : AT(. - KERNEL_VIRT_OFFSET) { + *(COMMON) + *(.bss*) + } + . = ALIGN(4K); + PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET); + + PROVIDE(__kernel_end = .); +}; diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ce59130f..a840e818 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -66,6 +66,9 @@ acpi-system = { git = "https://github.com/alnyan/acpi-system.git" } ygg_driver_nvme = { path = "driver/block/nvme" } kernel-arch-x86_64 = { path = "arch/x86_64" } +[target.'cfg(target_arch = "x86")'.dependencies] +kernel-arch-i686 = { path = "arch/i686" } + [build-dependencies] prettyplease = "0.2.15" abi-generator = { path = "../tool/abi-generator" } @@ -75,6 +78,7 @@ abi-generator = { path = "../tool/abi-generator" } aarch64-cpu = "9.4.0" device-tree = { path = "lib/device-tree" } kernel-arch-aarch64 = { path = "arch/aarch64" } +kernel-arch-i686 = { path = "arch/i686" } [features] default = ["fb_console"] diff --git a/kernel/arch/Cargo.toml b/kernel/arch/Cargo.toml index 78a41e21..efda492d 100644 --- a/kernel/arch/Cargo.toml +++ b/kernel/arch/Cargo.toml @@ -11,6 +11,9 @@ kernel-arch-x86_64 = { path = "x86_64" } [target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies] kernel-arch-aarch64 = { path = "aarch64" } +[target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies] +kernel-arch-i686 = { path = "i686" } + [target.'cfg(not(target_os = "none"))'.dependencies] kernel-arch-hosted = { path = "hosted" } diff --git a/kernel/arch/i686/Cargo.toml b/kernel/arch/i686/Cargo.toml new file mode 100644 index 00000000..2b32c631 --- /dev/null +++ b/kernel/arch/i686/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "kernel-arch-i686" +version = "0.1.0" +edition = "2021" + +[dependencies] +yggdrasil-abi = { path = "../../../lib/abi" } +kernel-arch-interface = { path = "../interface" } +libk-mm-interface = { path = "../../libk/libk-mm/interface" } +device-api = { path = "../../lib/device-api", features = ["derive"] } + +bitflags = "2.6.0" +static_assertions = "1.1.0" +tock-registers = "0.8.1" + +log = "*" diff --git a/kernel/arch/i686/src/context.S b/kernel/arch/i686/src/context.S new file mode 100644 index 00000000..8af4a888 --- /dev/null +++ b/kernel/arch/i686/src/context.S @@ -0,0 +1,136 @@ +// vi: set ft=asm : + +.macro SAVE_TASK_STATE + push %edi + push %esi + push %ebp + push %ebx +.endm + +.macro LOAD_TASK_STATE + pop %ebx + pop %ebp + pop %esi + pop %edi +.endm + +.section .text + +.global __i686_task_enter_kernel +.global __i686_task_enter_user +.global __i686_task_enter_from_fork +.global __i686_switch_task +.global __i686_enter_task +.global __i686_switch_and_drop + +__i686_task_enter_kernel: + pop %eax // Argument + pop %ecx // Entry + + // Enable IRQ in EFLAGS + pushfl + pop %edx + or $(1 << 9), %edx + + // Setup iret + push %edx // eflags + pushl $0x08 // cs + push %ecx // eip + + iret + +__i686_task_enter_user: + pop %edx // User %esp + pop %ecx // entry + pop %eax // flags + + // Setup iret + + // %ss:%esp + pushl $0x23 + push %edx + + // %eflags + push %eax + + // %cs:%eip + pushl $0x1B + push %ecx + + mov $0x23, %bx + mov %bx, %ds + mov %bx, %es + mov %bx, %fs + mov %bx, %gs + + iret + +__i686_task_enter_from_fork: + jmp . + +__i686_switch_task: + // %esp + 0: return + // %esp + 4: destination + // %esp + 8: source + mov 4(%esp), %eax + mov 8(%esp), %ecx + + SAVE_TASK_STATE + + // TODO TSS + // Store stack to "from" context + mov %esp, (%ecx) + + // Load stack from "to" context + mov (%eax), %esp + + LOAD_TASK_STATE + + // TODO TSS + + ret + +__i686_enter_task: + // %esp + 0: return + // %esp + 4: destination + + // Switch to destination stack + mov 4(%esp), %eax + mov (%eax), %esp + + // TODO TSS + + LOAD_TASK_STATE + + ret + +__i686_switch_and_drop: + // %esp + 0: return + // %esp + 4: destination + // %esp + 8: thread to drop + + mov 4(%esp), %eax + // Switch to stack + mov (%eax), %esp + + LOAD_TASK_STATE + // TODO actually drop the thread + + ret + # // TSS.RSP0 + # mov 8(%rdi), %rax + # // Kernel stack + # mov 0(%rdi), %rdi + + # mov %rdi, %rsp + + # // Load TSS.RSP0 + # mov %gs:(8), %rdi + # mov %rax, 4(%rdi) + + # mov %rsi, %rdi + # call __arch_drop_thread + + # LOAD_TASK_STATE + + # ret diff --git a/kernel/arch/i686/src/context.rs b/kernel/arch/i686/src/context.rs new file mode 100644 index 00000000..e6f19015 --- /dev/null +++ b/kernel/arch/i686/src/context.rs @@ -0,0 +1,395 @@ +use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData}; + +use kernel_arch_interface::{ + mem::{KernelTableManager, PhysicalMemoryAllocator}, + task::{StackBuilder, TaskContext, TaskFrame, UserContextInfo}, +}; +use libk_mm_interface::address::{AsPhysicalAddress, PhysicalAddress}; +use tock_registers::interfaces::Writeable; +use yggdrasil_abi::{arch::SavedFrame, error::Error}; + +use crate::{gdt::TSS, mem::KERNEL_TABLES, registers::CR3}; + +#[repr(C)] +pub struct ExceptionFrame { + pub eax: u32, + pub ecx: u32, + pub edx: u32, + pub ebx: u32, + pub ebp: u32, + pub esi: u32, + pub edi: u32, + + pub exc_number: u32, + pub exc_code: u32, + + pub eip: u32, + pub cs: u32, + pub eflags: u32, + esp: u32, + ss: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct SyscallFrame { + pub eax: usize, + // ebx, ecx, edx, esi, edi, ebp + pub args: [usize; 6], + + pub eip: u32, + pub cs: u32, + pub eflags: u32, + pub esp: u32, + pub ss: u32, +} + +#[repr(C)] +pub struct InterruptFrame { + pub eax: u32, + pub ecx: u32, + pub edx: u32, + pub ebx: u32, + pub ebp: u32, + pub esi: u32, + pub edi: u32, + + pub irq_number: u32, + + pub eip: u32, + pub cs: u32, + pub eflags: u32, + esp: u32, + ss: u32, +} + +#[repr(C, align(0x10))] +struct Inner { + // 0x00 + sp: usize, +} + +#[allow(dead_code)] +pub struct TaskContextImpl< + K: KernelTableManager, + PA: PhysicalMemoryAllocator
, +> { + inner: UnsafeCell, + stack_base_phys: PhysicalAddress, + stack_size: usize, + + cr3: u32, + tss_esp0: u32, + + _pd: PhantomData<(K, PA)>, +} + +impl> + TaskContext for TaskContextImpl +{ + const SIGNAL_STACK_EXTRA_ALIGN: usize = 0; + const USER_STACK_EXTRA_ALIGN: usize = 0; + + fn user(context: UserContextInfo) -> Result { + const USER_TASK_PAGES: usize = 8; + + let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?; + let stack_base = stack_base_phys.raw_virtualize::(); + + let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000); + + stack.push(0x200); + stack.push(context.entry as _); + stack.push(context.stack_pointer); + + setup_common_context(&mut stack, __i686_task_enter_user as _); + + let sp = stack.build(); + let esp0 = stack_base + USER_TASK_PAGES * 0x1000; + + Ok(Self { + inner: UnsafeCell::new(Inner { sp }), + stack_base_phys, + stack_size: USER_TASK_PAGES * 0x1000, + + tss_esp0: esp0 as _, + cr3: context.address_space.try_into().unwrap(), + + _pd: PhantomData, + }) + } + + fn kernel( + entry: extern "C" fn(usize) -> !, + arg: usize, + ) -> Result { + const KERNEL_TASK_PAGES: usize = 32; + + let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?; + let stack_base = stack_base_phys.raw_virtualize::(); + + let mut stack = StackBuilder::new(stack_base, KERNEL_TASK_PAGES * 0x1000); + + // Entry and argument + stack.push(entry as _); + stack.push(arg); + + // XXX + setup_common_context(&mut stack, __i686_task_enter_kernel as _); + + let sp = stack.build(); + + // TODO stack is leaked + + Ok(Self { + inner: UnsafeCell::new(Inner { sp }), + stack_base_phys, + stack_size: KERNEL_TASK_PAGES * 0x1000, + + tss_esp0: 0, + cr3: unsafe { KERNEL_TABLES.as_physical_address() } + .try_into_u32() + .unwrap(), + + _pd: PhantomData, + }) + } + + unsafe fn switch(&self, from: &Self) { + let dst = self.inner.get(); + let src = from.inner.get(); + + if dst != src { + TSS.esp0 = self.tss_esp0; + CR3.set(self.cr3); + + __i686_switch_task(dst, src); + } + } + + unsafe fn enter(&self) -> ! { + TSS.esp0 = self.tss_esp0; + CR3.set(self.cr3); + + __i686_enter_task(self.inner.get()) + } + + unsafe fn switch_and_drop(&self, thread: *const ()) { + TSS.esp0 = self.tss_esp0; + CR3.set(self.cr3); + + __i686_switch_and_drop(self.inner.get(), thread) + } +} + +fn setup_common_context(builder: &mut StackBuilder, entry: usize) { + builder.push(entry); + + builder.push(0); // %edi + builder.push(0); // %esi + builder.push(0); // %ebp + builder.push(0); // %ebx +} + +extern "C" { + fn __i686_task_enter_kernel(); + fn __i686_task_enter_user(); + fn __i686_task_enter_from_fork(); + fn __i686_enter_task(to: *mut Inner) -> !; + fn __i686_switch_task(to: *mut Inner, from: *mut Inner); + fn __i686_switch_and_drop(to: *mut Inner, from: *const ()); +} + +impl TaskFrame for SyscallFrame { + fn store(&self) -> SavedFrame { + SavedFrame { + eax: self.eax as _, + ecx: self.args[1] as _, + edx: self.args[2] as _, + ebx: self.args[0] as _, + ebp: self.args[5] as _, + esi: self.args[3] as _, + edi: self.args[4] as _, + + user_ip: self.eip, + user_sp: self.esp, + eflags: self.eflags, + } + } + + fn restore(&mut self, saved: &SavedFrame) { + todo!() + } + + fn user_sp(&self) -> usize { + todo!() + } + + fn user_ip(&self) -> usize { + todo!() + } + + fn argument(&self) -> u64 { + todo!() + } + + fn set_user_sp(&mut self, value: usize) { + self.esp = value as _; + } + + fn set_user_ip(&mut self, value: usize) { + self.eip = value as _; + } + + fn set_argument(&mut self, value: u64) { + // TODO implement ABI for passing 64-bit values via EAX/EDX + if value & (1 << 63) != 0 { + assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + self.eax = value as usize; + } + + fn set_single_step(&mut self, step: bool) { + todo!() + } + + fn set_return_value(&mut self, value: u64) { + // TODO implement ABI for returning 64-bit values via EAX/EDX + if value & (1 << 63) != 0 { + assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + self.eax = value as usize; + } +} + +impl TaskFrame for InterruptFrame { + fn store(&self) -> SavedFrame { + SavedFrame { + eax: self.eax, + ecx: self.ecx, + edx: self.edx, + ebx: self.ebx, + ebp: self.ebp, + esi: self.esi, + edi: self.edi, + + user_ip: self.eip, + user_sp: self.esp, + eflags: self.eflags, + } + } + + fn restore(&mut self, saved: &SavedFrame) { + todo!() + } + + fn user_sp(&self) -> usize { + todo!() + } + + fn user_ip(&self) -> usize { + todo!() + } + + fn argument(&self) -> u64 { + todo!() + } + + fn set_user_sp(&mut self, value: usize) { + self.esp = value as u32; + } + + fn set_user_ip(&mut self, value: usize) { + self.eip = value as u32; + } + + fn set_argument(&mut self, value: u64) { + // TODO implement ABI for returning 64-bit values via EAX/EDX + if value & (1 << 63) != 0 { + assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + self.eax = value as u32; + } + + fn set_single_step(&mut self, step: bool) { + todo!() + } + + fn set_return_value(&mut self, value: u64) { + // TODO implement ABI for returning 64-bit values via EAX/EDX + if value & (1 << 63) != 0 { + assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + self.eax = value as u32; + } +} + +impl TaskFrame for ExceptionFrame { + fn store(&self) -> SavedFrame { + SavedFrame { + eax: self.eax, + ecx: self.ecx, + edx: self.edx, + ebx: self.ebx, + ebp: self.ebp, + esi: self.esi, + edi: self.edi, + + user_ip: self.eip, + user_sp: self.esp, + eflags: self.eflags, + } + } + + fn restore(&mut self, saved: &SavedFrame) { + todo!() + } + + fn user_sp(&self) -> usize { + todo!() + } + + fn user_ip(&self) -> usize { + todo!() + } + + fn argument(&self) -> u64 { + todo!() + } + + fn set_user_sp(&mut self, value: usize) { + self.esp = value as u32; + } + + fn set_user_ip(&mut self, value: usize) { + self.eip = value as u32; + } + + fn set_argument(&mut self, value: u64) { + // TODO implement ABI for returning 64-bit values via EAX/EDX + if value & (1 << 63) != 0 { + assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + self.eax = value as u32; + } + + fn set_single_step(&mut self, step: bool) { + todo!() + } + + fn set_return_value(&mut self, value: u64) { + // TODO implement ABI for returning 64-bit values via EAX/EDX + if value & (1 << 63) != 0 { + assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + self.eax = value as u32; + } +} + +global_asm!(include_str!("context.S"), options(att_syntax)); diff --git a/kernel/arch/i686/src/gdt.rs b/kernel/arch/i686/src/gdt.rs new file mode 100644 index 00000000..78f6961f --- /dev/null +++ b/kernel/arch/i686/src/gdt.rs @@ -0,0 +1,71 @@ +#[allow(dead_code)] +#[repr(C, packed)] +pub struct Tss { + prev_tss: u32, + pub esp0: u32, + pub ss0: u16, + _res0: u16, + esp1: u32, + ss1: u16, + _res1: u16, + esp2: u32, + ss2: u16, + _res2: u16, + cr3: u32, + eip: u32, + eflags: u32, + eax: u32, + ecx: u32, + edx: u32, + ebx: u32, + esp: u32, + ebp: u32, + esi: u32, + edi: u32, + es: u32, + cs: u32, + ss: u32, + ds: u32, + fs: u32, + gs: u32, + ldt: u32, + trap: u16, + iomap_base: u16, +} + +impl Tss { + const NULL: Self = Self { + prev_tss: 0, + esp0: 0, + ss0: 0x10, + _res0: 0, + esp1: 0, + ss1: 0, + _res1: 0, + esp2: 0, + ss2: 0, + _res2: 0, + cr3: 0, + eip: 0, + eflags: 0, + eax: 0, + ecx: 0, + edx: 0, + ebx: 0, + esp: 0, + ebp: 0, + esi: 0, + edi: 0, + es: 0, + cs: 0, + ss: 0, + ds: 0, + fs: 0, + gs: 0, + ldt: 0, + trap: 0, + iomap_base: 0, + }; +} + +pub static mut TSS: Tss = Tss::NULL; diff --git a/kernel/arch/i686/src/lib.rs b/kernel/arch/i686/src/lib.rs new file mode 100644 index 00000000..dc27b965 --- /dev/null +++ b/kernel/arch/i686/src/lib.rs @@ -0,0 +1,113 @@ +#![feature(never_type, naked_functions, asm_const)] +#![no_std] + +extern crate alloc; + +use core::ptr::{addr_of_mut, null_mut}; + +use alloc::vec::Vec; +use device_api::interrupt::{LocalInterruptController, MessageInterruptController}; +use kernel_arch_interface::{ + cpu::{CpuImpl, IpiQueue}, + task::Scheduler, + Architecture, +}; + +pub mod context; +pub mod gdt; +pub mod mem; +pub mod registers; + +pub use context::TaskContextImpl; +pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl}; + +pub struct ArchitectureImpl; + +#[repr(C)] +pub struct PerCpuData {} + +static mut CPU: *mut () = null_mut(); + +#[naked] +extern "C" fn idle_task(_: usize) -> ! { + unsafe { + core::arch::asm!( + r#" + 1: + nop + jmp 1b + "#, + options(att_syntax, noreturn) + ); + } +} + +impl Architecture for ArchitectureImpl { + type PerCpuData = PerCpuData; + + unsafe fn init_local_cpu(id: Option, data: Self::PerCpuData) { + use alloc::boxed::Box; + + let cpu = Box::leak(Box::new(CpuImpl::::new( + id.expect("x86_64 required manual CPU ID set"), + data, + ))); + + cpu.set_local(); + } + + unsafe fn set_interrupt_mask(mask: bool) -> bool { + let old = Self::interrupt_mask(); + if mask { + core::arch::asm!("cli"); + } else { + core::arch::asm!("sti"); + } + old + } + + fn interrupt_mask() -> bool { + let mut flags: u32; + unsafe { + core::arch::asm!("pushfl; pop {0:e}", out(reg) flags, options(att_syntax)); + } + // If IF is zero, interrupts are disabled (masked) + flags & (1 << 9) == 0 + } + + fn wait_for_interrupt() { + unsafe { + core::arch::asm!("hlt"); + } + } + + unsafe fn init_ipi_queues(_queues: Vec>) {} + + fn local_cpu() -> *mut () { + unsafe { CPU } + } + + fn cpu_index() -> u32 { + 0 + } + + unsafe fn set_local_cpu(cpu: *mut ()) { + CPU = cpu; + } + + fn cpu_count() -> usize { + 1 + } + + fn message_interrupt_controller() -> &'static dyn MessageInterruptController { + loop {} + } + + fn local_interrupt_controller() -> &'static dyn LocalInterruptController { + loop {} + } + + fn idle_task() -> extern "C" fn(usize) -> ! { + idle_task + } +} diff --git a/kernel/arch/i686/src/mem/fixed.rs b/kernel/arch/i686/src/mem/fixed.rs new file mode 100644 index 00000000..19e560cd --- /dev/null +++ b/kernel/arch/i686/src/mem/fixed.rs @@ -0,0 +1,87 @@ +use kernel_arch_interface::KERNEL_VIRT_OFFSET; +use libk_mm_interface::address::PhysicalAddress; + +use crate::mem::KERNEL_TABLES; + +use super::table::{PageEntry, PageTable, L0, L3}; + +pub const KERNEL_SPLIT_L0: usize = KERNEL_VIRT_OFFSET >> 22; +pub const DYNAMIC_MAP_COUNT: usize = 64; +pub const FIXED_MAP_COUNT: usize = 1024 - (KERNEL_SPLIT_L0 + DYNAMIC_MAP_COUNT); +pub const MAX_FIXED_PHYSICAL: PhysicalAddress = + PhysicalAddress::from_u64((FIXED_MAP_COUNT as u64) << 22); + +pub struct FixedTables { + pub l0: KernelL0, + pub dynamic: KernelDynamic, +} + +#[repr(C, align(0x1000))] +pub struct KernelL0 { + pub lower: [PageEntry; KERNEL_SPLIT_L0], + pub kernel: [PageEntry; FIXED_MAP_COUNT], + pub dynamic: [PageEntry; DYNAMIC_MAP_COUNT], +} + +pub struct KernelDynamic { + l3s: [PageTable; DYNAMIC_MAP_COUNT], + refcounts: [u32; DYNAMIC_MAP_COUNT], +} + +impl FixedTables { + pub const fn zeroed() -> Self { + Self { + l0: KernelL0::zeroed(), + dynamic: KernelDynamic::zeroed(), + } + } + + pub fn virtualize(&mut self, address: PhysicalAddress) -> usize { + if address < MAX_FIXED_PHYSICAL { + // It's a fixed address + address.into_u64() as usize + KERNEL_VIRT_OFFSET + } else { + loop {} + } + } + + pub fn physicalize(&mut self, address: usize) -> Option { + if address < KERNEL_VIRT_OFFSET { + return None; + } + + if address < KERNEL_VIRT_OFFSET + MAX_FIXED_PHYSICAL.into_u64() as usize { + // It's a fixed address + Some(PhysicalAddress::from_usize(address - KERNEL_VIRT_OFFSET)) + } else { + loop {} + } + } +} + +impl KernelL0 { + pub const fn zeroed() -> Self { + Self { + lower: [PageEntry::INVALID; KERNEL_SPLIT_L0], + kernel: [PageEntry::INVALID; FIXED_MAP_COUNT], + dynamic: [PageEntry::INVALID; DYNAMIC_MAP_COUNT], + } + } +} + +impl KernelDynamic { + pub const fn zeroed() -> Self { + Self { + l3s: [PageTable::zeroed(); DYNAMIC_MAP_COUNT], + refcounts: [0; DYNAMIC_MAP_COUNT], + } + } +} + +pub fn clone_kernel_tables(dst: &mut PageTable) { + for (i, entry) in unsafe { KERNEL_TABLES.l0.kernel.iter().enumerate() } { + dst[i + KERNEL_SPLIT_L0] = *entry; + } + + // TODO dynamic entries +} diff --git a/kernel/arch/i686/src/mem/mod.rs b/kernel/arch/i686/src/mem/mod.rs new file mode 100644 index 00000000..e3dbed7b --- /dev/null +++ b/kernel/arch/i686/src/mem/mod.rs @@ -0,0 +1,79 @@ +use fixed::FixedTables; +use kernel_arch_interface::{ + mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping}, + KERNEL_VIRT_OFFSET, +}; +use libk_mm_interface::{address::PhysicalAddress, KernelImageObject}; +use table::{PageAttributes, PageEntry}; +use yggdrasil_abi::error::Error; + +pub mod fixed; +pub mod process; +pub mod table; + +pub use process::ProcessAddressSpaceImpl; + +#[derive(Debug)] +pub struct KernelTableManagerImpl; + +#[link_section = ".data.tables"] +pub static mut KERNEL_TABLES: KernelImageObject = + unsafe { KernelImageObject::new(FixedTables::zeroed()) }; + +impl KernelTableManager for KernelTableManagerImpl { + unsafe fn map_device_pages( + base: u64, + count: usize, + _attrs: DeviceMemoryAttributes, + ) -> Result, Error> { + // TODO page align up + let end = base + count as u64; + if end < fixed::MAX_FIXED_PHYSICAL.into_u64() { + // 1:1 + let address = Self::virtualize(base); + Ok(RawDeviceMemoryMapping::from_raw_parts( + address, address, 0, 0, + )) + } else { + loop {} + } + } + + unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping) { + loop {} + } + + fn virtualize(phys: u64) -> usize { + unsafe { KERNEL_TABLES.virtualize(PhysicalAddress::from_u64(phys)) } + } + + fn physicalize(virt: usize) -> u64 { + unsafe { KERNEL_TABLES.physicalize(virt) } + .expect("Invalid virtual address") + .into_u64() + } +} + +pub unsafe fn init_fixed_tables() { + // Unmap lower stuff + for (i, entry) in KERNEL_TABLES.l0.lower.iter_mut().enumerate() { + *entry = PageEntry::INVALID; + flush_tlb_entry(i << 22); + } + + // Map the rest of fixed translation + for (i, entry) in KERNEL_TABLES.l0.kernel.iter_mut().enumerate() { + let virt = KERNEL_VIRT_OFFSET + i << 22; + let phys = (i << 22) as u32; + *entry = PageEntry::block(PhysicalAddress::from_u32(phys), PageAttributes::WRITABLE); + flush_tlb_entry(virt); + } +} + +/// # Safety +/// +/// `address` must be page-aligned. +#[inline] +pub unsafe fn flush_tlb_entry(address: usize) { + core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax)); +} diff --git a/kernel/arch/i686/src/mem/process.rs b/kernel/arch/i686/src/mem/process.rs new file mode 100644 index 00000000..9a7f906b --- /dev/null +++ b/kernel/arch/i686/src/mem/process.rs @@ -0,0 +1,125 @@ +use core::marker::PhantomData; + +use kernel_arch_interface::KERNEL_VIRT_OFFSET; +use libk_mm_interface::{ + address::{AsPhysicalAddress, PhysicalAddress}, + pointer::PhysicalRefMut, + process::ProcessAddressSpaceManager, + table::{EntryLevel, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator}, +}; +use yggdrasil_abi::error::Error; + +use crate::{mem::flush_tlb_entry, KernelTableManagerImpl}; + +use super::{ + fixed::clone_kernel_tables, + table::{PageEntry, PageTable, L0, L3}, +}; + +#[repr(C)] +pub struct ProcessAddressSpaceImpl { + l0: PhysicalRefMut<'static, PageTable, KernelTableManagerImpl>, + _alloc: PhantomData, +} + +impl ProcessAddressSpaceManager for ProcessAddressSpaceImpl { + const UPPER_LIMIT_PFN: usize = KERNEL_VIRT_OFFSET >> L3::SHIFT; + const LOWER_LIMIT_PFN: usize = 32; + + fn new() -> Result { + let mut l0 = unsafe { + PhysicalRefMut::<'static, PageTable, KernelTableManagerImpl>::map( + TA::allocate_page_table()?, + ) + }; + + for i in 0..1024 { + l0[i] = PageEntry::INVALID; + } + + clone_kernel_tables(&mut l0); + + Ok(Self { + l0, + _alloc: PhantomData, + }) + } + + unsafe fn clear(&mut self) { + // TODO + // self.l0 + // .drop_range::(0..((Self::UPPER_LIMIT_PFN * L3::SIZE).page_index::())); + } + + fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> { + self.read_l3_entry(address).ok_or(Error::DoesNotExist) + } + + unsafe fn map_page( + &mut self, + address: usize, + physical: PhysicalAddress, + flags: MapAttributes, + ) -> Result<(), Error> { + self.write_l3_entry(address, PageEntry::page(physical, flags.into()), false) + } + + unsafe fn unmap_page(&mut self, address: usize) -> Result { + self.pop_l3_entry(address) + } + + fn as_address_with_asid(&self) -> u64 { + unsafe { self.l0.as_physical_address().into_u64() } + } +} + +impl ProcessAddressSpaceImpl { + // Write a single 4KiB entry + fn write_l3_entry( + &mut self, + virt: usize, + entry: PageEntry, + overwrite: bool, + ) -> Result<(), Error> { + let l0i = virt.page_index::(); + let l3i = virt.page_index::(); + + let mut l3 = self.l0.get_mut_or_alloc::(l0i)?; + + if l3[l3i].is_present() && !overwrite { + todo!(); + } + + l3[l3i] = entry; + unsafe { + flush_tlb_entry(virt); + } + + Ok(()) + } + + fn pop_l3_entry(&mut self, virt: usize) -> Result { + let l0i = virt.page_index::(); + let l3i = virt.page_index::(); + + let mut l3 = self.l0.get_mut(l0i).ok_or(Error::DoesNotExist)?; + let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?; + + l3[l3i] = PageEntry::INVALID; + unsafe { + flush_tlb_entry(virt); + } + + Ok(page) + } + + fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> { + let l0i = virt.page_index::(); + let l3i = virt.page_index::(); + + let l3 = self.l0.get(l0i)?; + let page = l3[l3i].as_page()?; + + Some((page, l3[l3i].attributes().into())) + } +} diff --git a/kernel/arch/i686/src/mem/table.rs b/kernel/arch/i686/src/mem/table.rs new file mode 100644 index 00000000..31e30496 --- /dev/null +++ b/kernel/arch/i686/src/mem/table.rs @@ -0,0 +1,210 @@ +use core::{ + marker::PhantomData, + ops::{Index, IndexMut}, +}; + +use bitflags::bitflags; +use libk_mm_interface::{ + address::{AsPhysicalAddress, PhysicalAddress}, + pointer::{PhysicalRef, PhysicalRefMut}, + table::{EntryLevel, MapAttributes, NextPageTable, NonTerminalEntryLevel, TableAllocator}, +}; +use yggdrasil_abi::error::Error; + +use crate::KernelTableManagerImpl; + +bitflags! { + /// Describes how each page table entry is mapped + pub struct PageAttributes: u32 { + /// When set, the mapping is considered valid and pointing somewhere + const PRESENT = 1 << 0; + /// For tables, allows writes to further translation levels, for pages/blocks, allows + /// writes to the region covered by the entry + const WRITABLE = 1 << 1; + /// When set for L2 entries, the mapping specifies a 2MiB page instead of a page table + /// reference + const BLOCK = 1 << 7; + /// For tables, allows user access to further translation levels, for pages/blocks, allows + /// user access to the region covered by the entry + const USER = 1 << 2; + } +} +// TODO stuff for PAE? + +#[derive(Debug, Clone, Copy)] +pub struct L3; +#[derive(Debug, Clone, Copy)] +pub struct L0; + +#[derive(Clone, Copy, Debug)] +pub struct PageEntry(u32, PhantomData); + +#[derive(Clone, Copy, Debug)] +#[repr(C, align(0x1000))] +pub struct PageTable { + data: [PageEntry; 1024], +} + +impl EntryLevel for L3 { + const SHIFT: usize = 12; +} + +impl EntryLevel for L0 { + const SHIFT: usize = 22; +} + +impl NonTerminalEntryLevel for L0 { + type NextLevel = L3; +} + +impl PageEntry { + pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self { + Self( + address.try_into_u32().unwrap() | (PageAttributes::PRESENT | attrs).bits(), + PhantomData, + ) + } + + pub fn as_page(&self) -> Option { + if self.0 & PageAttributes::PRESENT.bits() != 0 { + Some(PhysicalAddress::from_u32(self.0 & !0xFFF)) + } else { + None + } + } +} + +impl PageEntry { + pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self { + Self( + address.try_into_u32().unwrap() + | (PageAttributes::PRESENT | PageAttributes::BLOCK | attrs).bits(), + PhantomData, + ) + } + + pub fn table(address: PhysicalAddress, attrs: PageAttributes) -> Self { + Self( + address.try_into_u32().unwrap() | (PageAttributes::PRESENT | attrs).bits(), + PhantomData, + ) + } + + pub fn as_table(&self) -> Option { + if self.0 & PageAttributes::PRESENT.bits() != 0 + && self.0 & PageAttributes::BLOCK.bits() == 0 + { + Some(PhysicalAddress::from_u32(self.0 & !0xFFF)) + } else { + None + } + } +} + +impl PageEntry { + pub const INVALID: Self = Self(0, PhantomData); + + pub fn is_present(&self) -> bool { + self.0 & (1 << 0) != 0 + } + + pub fn attributes(&self) -> PageAttributes { + PageAttributes::from_bits_retain(self.0) + } +} + +impl PageTable { + pub const fn zeroed() -> Self { + Self { + data: [PageEntry::INVALID; 1024], + } + } + + pub fn new_zeroed<'a, TA: TableAllocator>( + ) -> Result, Error> { + let physical = TA::allocate_page_table()?; + let mut table = + unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) }; + + for i in 0..1024 { + table[i] = PageEntry::INVALID; + } + + Ok(table) + } +} + +impl NextPageTable for PageTable { + type NextLevel = PageTable; + type TableRef = PhysicalRef<'static, Self::NextLevel, KernelTableManagerImpl>; + type TableRefMut = PhysicalRefMut<'static, Self::NextLevel, KernelTableManagerImpl>; + + fn get(&self, index: usize) -> Option { + self[index] + .as_table() + .map(|addr| unsafe { PhysicalRef::map(addr) }) + } + + fn get_mut(&mut self, index: usize) -> Option { + self[index] + .as_table() + .map(|addr| unsafe { PhysicalRefMut::map(addr) }) + } + + fn get_mut_or_alloc( + &mut self, + index: usize, + ) -> Result { + let entry = self[index]; + + if let Some(table) = entry.as_table() { + Ok(unsafe { PhysicalRefMut::map(table) }) + } else { + let table = PageTable::new_zeroed::()?; + self[index] = PageEntry::::table( + unsafe { table.as_physical_address() }, + PageAttributes::WRITABLE | PageAttributes::USER, + ); + Ok(table) + } + } +} + +impl Index for PageTable { + type Output = PageEntry; + + fn index(&self, index: usize) -> &Self::Output { + &self.data[index] + } +} + +impl IndexMut for PageTable { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.data[index] + } +} + +impl From for PageAttributes { + fn from(value: MapAttributes) -> Self { + let mut res = PageAttributes::WRITABLE; + if value.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) { + res |= PageAttributes::USER; + } + res + } +} + +impl From for MapAttributes { + fn from(value: PageAttributes) -> Self { + let mut res = MapAttributes::empty(); + if value.contains(PageAttributes::USER) { + res |= MapAttributes::USER_READ; + if value.contains(PageAttributes::WRITABLE) { + res |= MapAttributes::USER_WRITE; + } + } + // TODO ??? + res |= MapAttributes::NON_GLOBAL; + res + } +} diff --git a/kernel/arch/i686/src/registers.rs b/kernel/arch/i686/src/registers.rs new file mode 100644 index 00000000..b10182c8 --- /dev/null +++ b/kernel/arch/i686/src/registers.rs @@ -0,0 +1,156 @@ +macro_rules! impl_read { + ($t:ident, $register:ty, $body:expr) => { + impl tock_registers::interfaces::Readable for $t { + type T = u32; + type R = $register; + + #[inline] + fn get(&self) -> u32 { + $body + } + } + }; +} + +macro_rules! impl_write { + ($t:ident, $register:ty, $value:ident, $body:expr) => { + impl tock_registers::interfaces::Writeable for $t { + type T = u32; + type R = $register; + + #[inline] + fn set(&self, $value: u32) { + $body + } + } + }; +} + +macro_rules! cr_impl_read { + ($t:ident, $cr:ident, $register:ty) => { + impl_read!($t, $register, { + let value: u32; + unsafe { + core::arch::asm!( + concat!("mov %", stringify!($cr), ", {0:e}"), + out(reg) value, + options(att_syntax) + ); + } + value + }); + }; +} + +macro_rules! cr_impl_write { + ($t:ident, $cr:ident, $register:ty) => { + impl_write!($t, $register, value, { + unsafe { + core::arch::asm!( + concat!("mov {0:e}, %", stringify!($cr)), + in(reg) value, + options(att_syntax) + ); + } + }); + }; +} + +mod cr0 { + use tock_registers::register_bitfields; + + register_bitfields! { + u32, + #[allow(missing_docs)] + pub CR0 [ + PG OFFSET(31) NUMBITS(1) [], + CD OFFSET(30) NUMBITS(1) [], + NW OFFSET(29) NUMBITS(1) [], + AM OFFSET(18) NUMBITS(1) [], + WP OFFSET(16) NUMBITS(1) [], + NE OFFSET(5) NUMBITS(1) [], + ET OFFSET(4) NUMBITS(1) [], + TS OFFSET(3) NUMBITS(1) [], + EM OFFSET(2) NUMBITS(1) [], + MP OFFSET(1) NUMBITS(1) [], + PE OFFSET(0) NUMBITS(1) [], + ] + } + + pub struct Reg; + + cr_impl_read!(Reg, cr0, CR0::Register); + cr_impl_write!(Reg, cr0, CR0::Register); + + /// x86-64 control register 0 + pub const CR0: Reg = Reg; +} + +mod cr3 { + use tock_registers::{interfaces::ReadWriteable, register_bitfields}; + + register_bitfields! { + u32, + #[allow(missing_docs)] + pub CR3 [ + ADDR OFFSET(12) NUMBITS(20) [], + ] + } + + pub struct Reg; + + cr_impl_read!(Reg, cr3, CR3::Register); + cr_impl_write!(Reg, cr3, CR3::Register); + + impl Reg { + pub fn set_address(&self, address: usize) { + assert_eq!(address & 0xFFF, 0); + self.modify(CR3::ADDR.val((address as u32) >> 12)) + } + } + + /// x86-64 control register 3 + pub const CR3: Reg = Reg; +} + +mod cr4 { + use tock_registers::register_bitfields; + + register_bitfields! { + u32, + #[allow(missing_docs)] + pub CR4 [ + /// If set, XSAVE and extended processor states are enabled + OSXSAVE OFFSET(18) NUMBITS(1) [], + /// Indicates OS support for FXSAVE and FXRSTOR instructions + OSFXSR OFFSET(9) NUMBITS(1) [], + /// Performance-Monitoring Counter enable + PCE OFFSET(8) NUMBITS(1) [], + /// If set, "page global" attribute is enabled + PGE OFFSET(7) NUMBITS(1) [], + /// Machine Check enable + MCE OFFSET(6) NUMBITS(1) [], + /// Physical Address Extension (enabled if 64-bit mode) + PAE OFFSET(5) NUMBITS(1) [], + /// Page Size Extension (should be enabled by yboot) + PSE OFFSET(4) NUMBITS(1) [], + /// Debugging extensions + DE OFFSET(3) NUMBITS(1) [], + TSD OFFSET(2) NUMBITS(1) [], + PVI OFFSET(1) NUMBITS(1) [], + VME OFFSET(0) NUMBITS(1) [], + ] + } + + pub struct Reg; + + cr_impl_read!(Reg, cr4, CR4::Register); + cr_impl_write!(Reg, cr4, CR4::Register); + + /// x86-64 control register 4 + pub const CR4: Reg = Reg; +} + +pub use cr0::CR0; +pub use cr3::CR3; +pub use cr4::CR4; diff --git a/kernel/arch/interface/src/lib.rs b/kernel/arch/interface/src/lib.rs index 7a55ab10..5053c3b2 100644 --- a/kernel/arch/interface/src/lib.rs +++ b/kernel/arch/interface/src/lib.rs @@ -16,6 +16,10 @@ pub mod sync; pub mod task; pub mod util; +#[cfg(any(target_pointer_width = "32", rust_analyzer))] +pub const KERNEL_VIRT_OFFSET: usize = 0xC0000000; + +#[cfg(any(target_pointer_width = "64", rust_analyzer))] pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; pub trait Architecture: Sized { diff --git a/kernel/arch/interface/src/mem/mod.rs b/kernel/arch/interface/src/mem/mod.rs index 1bad89d8..94484b50 100644 --- a/kernel/arch/interface/src/mem/mod.rs +++ b/kernel/arch/interface/src/mem/mod.rs @@ -64,6 +64,9 @@ pub trait KernelTableManager: Sized + fmt::Debug { /// /// Only meant to be called from "safer" wrappers like [RawDeviceMemoryMapping]. unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping); + + #[allow(unused)] + unsafe fn unmap_physical_address(virt: usize) {} } impl RawDeviceMemoryMapping { diff --git a/kernel/arch/interface/src/task.rs b/kernel/arch/interface/src/task.rs index cdf4ec2c..b56e9707 100644 --- a/kernel/arch/interface/src/task.rs +++ b/kernel/arch/interface/src/task.rs @@ -151,7 +151,7 @@ impl StackBuilder { if self.sp == self.base { panic!(); } - self.sp -= 8; + self.sp -= size_of::(); unsafe { (self.sp as *mut usize).write_volatile(value); } diff --git a/kernel/arch/src/lib.rs b/kernel/arch/src/lib.rs index abc65a97..8f9e80f1 100644 --- a/kernel/arch/src/lib.rs +++ b/kernel/arch/src/lib.rs @@ -26,6 +26,8 @@ cfg_if! { extern crate kernel_arch_aarch64 as imp; } else if #[cfg(target_arch = "x86_64")] { extern crate kernel_arch_x86_64 as imp; + } else if #[cfg(target_arch = "x86")] { + extern crate kernel_arch_i686 as imp; } else { compile_error!("Unsupported architecture"); } diff --git a/kernel/build.rs b/kernel/build.rs index f2f637cb..bfcc5896 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -51,16 +51,24 @@ fn load_abi_definitions>(path: P) -> String { content } -fn generate_syscall_dispatcher, P: AsRef>(abi_path: A, out_dir: P) { - let abi = load_abi_definitions(abi_path); - let abi: AbiBuilder = AbiBuilder::from_string( - &abi, +fn generate_syscall_dispatcher, P: AsRef>( + target_is_64bit: bool, + abi_path: A, + out_dir: P, +) { + let target_env = if target_is_64bit { TargetEnv { thin_pointer_width: TypeWidth::U64, fat_pointer_width: TypeWidth::U128, - }, - ) - .unwrap_fancy(&abi); + } + } else { + TargetEnv { + thin_pointer_width: TypeWidth::U32, + fat_pointer_width: TypeWidth::U64, + } + }; + let abi = load_abi_definitions(abi_path); + let abi: AbiBuilder = AbiBuilder::from_string(&abi, target_env).unwrap_fancy(&abi); let generated_dispatcher = out_dir.as_ref().join("generated_dispatcher.rs"); let file = prettyplease::unparse( @@ -74,12 +82,14 @@ fn generate_syscall_dispatcher, P: AsRef>(abi_path: A, out_ fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let target_is_64bit = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "64"; - generate_syscall_dispatcher("../lib/abi/def", out_dir); + generate_syscall_dispatcher(target_is_64bit, "../lib/abi/def", out_dir); println!("cargo:rerun-if-changed=build.rs"); match arch.as_str() { + "x86" => (), "x86_64" => build_x86_64(), "aarch64" => (), _ => panic!("Unknown target arch: {:?}", arch), diff --git a/kernel/driver/bus/pci/src/capability.rs b/kernel/driver/bus/pci/src/capability.rs index ffd33379..c03d6212 100644 --- a/kernel/driver/bus/pci/src/capability.rs +++ b/kernel/driver/bus/pci/src/capability.rs @@ -376,8 +376,9 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> { return Err(Error::InvalidOperation); } - self.space - .write_u32(self.offset + 8, (info.address >> 32) as u32); + todo!("FIXME: PCI 64-bit addresses"); + // self.space + // .write_u32(self.offset + 8, (info.address >> 32) as u32); } self.space.write_u32(self.offset + 4, info.address as u32); diff --git a/kernel/driver/bus/pci/src/lib.rs b/kernel/driver/bus/pci/src/lib.rs index 8f21f9b6..3ac7865d 100644 --- a/kernel/driver/bus/pci/src/lib.rs +++ b/kernel/driver/bus/pci/src/lib.rs @@ -14,6 +14,7 @@ use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciInterrupt, PciInterruptR use device_api::Device; use libk_mm::address::PhysicalAddress; use libk_util::{sync::IrqSafeSpinlock, OneTimeInit}; +use space::legacy; use yggdrasil_abi::error::Error; pub mod capability; @@ -21,7 +22,9 @@ pub mod device; mod space; pub use space::{ - ecam::PciEcam, PciConfigSpace, PciConfigurationSpace, PciLegacyConfigurationSpace, + ecam::PciEcam, + legacy::{LegacyPciAccess, PciLegacyConfigurationSpace}, + PciConfigSpace, PciConfigurationSpace, }; bitflags! { @@ -242,7 +245,7 @@ impl PciBusSegment { PciEcam::probe_raw_parts(ecam_phys_base, self.info.bus_number_start, address)? } .map(PciConfigSpace::Ecam)), - None => todo!(), + None => Ok(PciLegacyConfigurationSpace::probe(address)?.map(PciConfigSpace::Legacy)), } } @@ -409,6 +412,29 @@ impl PciBusManager { Ok(()) } + pub fn add_legacy_segment(access: &'static dyn LegacyPciAccess) -> Result<(), Error> { + legacy::PCI.init(access); + + let mut bus_segment = PciBusSegment { + info: Arc::new(PciSegmentInfo { + segment_number: 0, + bus_number_start: 0, + bus_number_end: 255, + ecam_phys_base: None, + irq_translation_map: BTreeMap::new(), + has_msi: false, + }), + allocator: None, + devices: Vec::new(), + }; + + let mut this = PCI_MANAGER.lock(); + bus_segment.enumerate()?; + this.segments.push(bus_segment); + + Ok(()) + } + /// Enumerates a bus segment provided by ACPI MCFG table entry #[cfg(target_arch = "x86_64")] pub fn add_segment_from_mcfg(entry: &McfgEntry) -> Result<(), Error> { @@ -512,14 +538,14 @@ impl PciConfigurationSpace for PciConfigSpace { fn read_u32(&self, offset: usize) -> u32 { match self { Self::Ecam(ecam) => ecam.read_u32(offset), - _ => todo!(), + Self::Legacy(legacy) => legacy.read_u32(offset), } } fn write_u32(&self, offset: usize, value: u32) { match self { Self::Ecam(ecam) => ecam.write_u32(offset, value), - _ => todo!(), + Self::Legacy(legacy) => legacy.write_u32(offset, value), } } } diff --git a/kernel/driver/bus/pci/src/space/legacy.rs b/kernel/driver/bus/pci/src/space/legacy.rs new file mode 100644 index 00000000..b7095aa4 --- /dev/null +++ b/kernel/driver/bus/pci/src/space/legacy.rs @@ -0,0 +1,46 @@ +use libk_util::{sync::IrqSafeSpinlock, OneTimeInit}; +use yggdrasil_abi::error::Error; + +use crate::{PciAddress, PciConfigurationSpace}; + +/// Provides access to the legacy (port I/O-driven) PCI configuration space +#[derive(Clone, Debug)] +#[repr(transparent)] +pub struct PciLegacyConfigurationSpace { + address: PciAddress, +} + +pub trait LegacyPciAccess { + fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32; + fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32); +} + +impl PciConfigurationSpace for PciLegacyConfigurationSpace { + fn read_u32(&self, offset: usize) -> u32 { + PCI.get().read_u32( + self.address.bus, + self.address.device, + self.address.function, + offset as _, + ) + } + + fn write_u32(&self, offset: usize, value: u32) { + PCI.get().write_u32( + self.address.bus, + self.address.device, + self.address.function, + offset as _, + value, + ) + } +} + +impl PciLegacyConfigurationSpace { + pub fn probe(address: PciAddress) -> Result, Error> { + let this = PciLegacyConfigurationSpace { address }; + Ok(if this.is_valid() { Some(this) } else { None }) + } +} + +pub(crate) static PCI: OneTimeInit<&'static dyn LegacyPciAccess> = OneTimeInit::new(); diff --git a/kernel/driver/bus/pci/src/space/mod.rs b/kernel/driver/bus/pci/src/space/mod.rs index 19e08fc1..3edb7262 100644 --- a/kernel/driver/bus/pci/src/space/mod.rs +++ b/kernel/driver/bus/pci/src/space/mod.rs @@ -1,9 +1,11 @@ use alloc::sync::Arc; +use legacy::PciLegacyConfigurationSpace; use super::{PciAddress, PciBaseAddress, PciCapability, PciCapabilityId, PciEcam}; use crate::{device::PciInterruptPin, PciCommandRegister, PciStatusRegister}; pub(super) mod ecam; +pub(super) mod legacy; macro_rules! pci_config_field_getter { ($self:ident, u32, $offset:expr) => { @@ -53,21 +55,13 @@ macro_rules! pci_config_field { }; } -/// Provides access to the legacy (port I/O-driven) PCI configuration space -#[derive(Debug)] -#[repr(transparent)] -pub struct PciLegacyConfigurationSpace { - #[allow(unused)] - address: PciAddress, -} - /// Describes a configuration space access method for a PCI device #[derive(Debug, Clone)] pub enum PciConfigSpace { /// Legacy configuration space. /// /// See [PciLegacyConfigurationSpace]. - Legacy(PciAddress), + Legacy(PciLegacyConfigurationSpace), /// Enhanced Configuration Access Mechanism (PCIe). /// diff --git a/kernel/driver/input/src/lib.rs b/kernel/driver/input/src/lib.rs index c9c4447a..bb168806 100644 --- a/kernel/driver/input/src/lib.rs +++ b/kernel/driver/input/src/lib.rs @@ -6,13 +6,14 @@ use core::task::{Context, Poll}; use alloc::boxed::Box; use async_trait::async_trait; -use libk::vfs::{CharDevice, FileReadiness}; -use libk_util::ring::LossyRingQueue; +use libk::vfs::{CharDevice, FileReadiness, TerminalInput}; +use libk_util::{ring::LossyRingQueue, sync::spin_rwlock::IrqSafeRwLock, StaticVector}; use yggdrasil_abi::{ error::Error, io::{DeviceRequest, KeyboardKeyEvent}, }; +#[derive(Clone, Copy)] pub struct KeyboardDevice; impl FileReadiness for KeyboardDevice { diff --git a/kernel/driver/usb/xhci/src/lib.rs b/kernel/driver/usb/xhci/src/lib.rs index 17a527fb..34cfce75 100644 --- a/kernel/driver/usb/xhci/src/lib.rs +++ b/kernel/driver/usb/xhci/src/lib.rs @@ -100,11 +100,12 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> { cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER; info.config_space.set_command(cmd.bits()); - let regs = unsafe { xhci_lib::Registers::new(bar0.into(), Mapper::new()) }; - let xhci = Box::leak(Box::new(Xhci::new(regs)?)); + todo!() + // let regs = unsafe { xhci_lib::Registers::new(bar0.into(), Mapper::new()) }; + // let xhci = Box::leak(Box::new(Xhci::new(regs)?)); - info.init_interrupts(PreferredInterruptMode::Msi)?; - info.map_interrupt(InterruptAffinity::Any, xhci)?; + // info.init_interrupts(PreferredInterruptMode::Msi)?; + // info.map_interrupt(InterruptAffinity::Any, xhci)?; - Ok(xhci) + // Ok(xhci) } diff --git a/kernel/lib/vmalloc/src/lib.rs b/kernel/lib/vmalloc/src/lib.rs index 716265ea..13ec7c87 100644 --- a/kernel/lib/vmalloc/src/lib.rs +++ b/kernel/lib/vmalloc/src/lib.rs @@ -15,8 +15,10 @@ use core::ops::Range; use discrete_range_map::{DiscreteRangeMap, InclusiveInterval, InclusiveRange}; use yggdrasil_abi::error::Error; -#[cfg(target_pointer_width = "64")] +#[cfg(any(target_pointer_width = "64", rust_analyzer))] type PfnIndex = u64; +#[cfg(any(target_pointer_width = "32", rust_analyzer))] +type PfnIndex = u32; /// Metadata associated with an allocated memory region. The [Eq] trait is used to coalesce "equal" /// regions if they "touch". diff --git a/kernel/libk/libk-mm/interface/src/address.rs b/kernel/libk/libk-mm/interface/src/address.rs index 1d81bffa..30aaccbb 100644 --- a/kernel/libk/libk-mm/interface/src/address.rs +++ b/kernel/libk/libk-mm/interface/src/address.rs @@ -2,6 +2,7 @@ use core::{ fmt, iter::Step, mem::align_of, + num::TryFromIntError, ops::{Add, Sub}, }; @@ -57,10 +58,23 @@ impl PhysicalAddress { Self(value) } + pub const fn from_u32(value: u32) -> Self { + Self(value as u64) + } + + #[cfg(any(target_pointer_width = "64", rust_analyzer))] pub const fn into_usize(self) -> usize { self.0 as usize } + pub fn try_into_usize(self) -> Result { + self.0.try_into() + } + + pub fn try_into_u32(self) -> Result { + self.0.try_into() + } + pub const fn into_u64(self) -> u64 { self.0 } @@ -144,6 +158,7 @@ impl From for u64 { } } +#[cfg(any(target_pointer_width = "64", rust_analyzer))] impl From for usize { fn from(addr: PhysicalAddress) -> usize { addr.0 as usize diff --git a/kernel/libk/libk-mm/interface/src/pointer.rs b/kernel/libk/libk-mm/interface/src/pointer.rs index 0efea751..67bcbfee 100644 --- a/kernel/libk/libk-mm/interface/src/pointer.rs +++ b/kernel/libk/libk-mm/interface/src/pointer.rs @@ -90,6 +90,12 @@ impl fmt::Pointer for PhysicalRefMut<'_, T, K> } } +impl Drop for PhysicalRefMut<'_, T, K> { + fn drop(&mut self) { + unsafe { K::unmap_physical_address(self.as_address()) } + } +} + // PhysicalRef: same as PhysicalRefMut, except immutable impl<'a, T: Sized, K: KernelTableManager> PhysicalRef<'a, T, K> { @@ -145,6 +151,12 @@ impl Deref for PhysicalRef<'_, T, K> { } } +impl Drop for PhysicalRef<'_, T, K> { + fn drop(&mut self) { + unsafe { K::unmap_physical_address(self.as_address()) } + } +} + unsafe fn virtualize_raw<'a, T: Sized, K: KernelTableManager>( physical: PhysicalAddress, ) -> &'a mut T { diff --git a/kernel/libk/libk-mm/interface/src/table.rs b/kernel/libk/libk-mm/interface/src/table.rs index 9635aa70..683bbec4 100644 --- a/kernel/libk/libk-mm/interface/src/table.rs +++ b/kernel/libk/libk-mm/interface/src/table.rs @@ -5,6 +5,11 @@ use yggdrasil_abi::error::Error; use super::address::PhysicalAddress; +#[cfg(not(target_arch = "x86"))] +const TABLE_SIZE_MASK: usize = 0x1FF; +#[cfg(target_arch = "x86")] +const TABLE_SIZE_MASK: usize = 0x3FF; + /// Interface for a single level of address translation pub trait EntryLevel: Copy { /// The right shift needed to obtain an index of an entry at this level from an address @@ -36,7 +41,7 @@ bitflags! { } pub const fn page_index(address: usize) -> usize { - address >> T::SHIFT & 0x1FF + address >> T::SHIFT & TABLE_SIZE_MASK } pub const fn page_offset(address: usize) -> usize { diff --git a/kernel/libk/libk-mm/src/lib.rs b/kernel/libk/libk-mm/src/lib.rs index 7ad405c4..de646bcb 100644 --- a/kernel/libk/libk-mm/src/lib.rs +++ b/kernel/libk/libk-mm/src/lib.rs @@ -50,7 +50,11 @@ impl TableAllocator for TableAllocatorImpl { // TODO find a way to integrate this nicely with Architecture? pub const L3_PAGE_SIZE: usize = 1 << 12; + +#[cfg(not(target_arch = "x86"))] pub const L2_PAGE_SIZE: usize = 1 << 21; +#[cfg(target_arch = "x86")] +pub const L2_PAGE_SIZE: usize = 1 << 22; pub trait PageProvider { fn get_page(&self, offset: u64) -> Result; diff --git a/kernel/libk/libk-mm/src/phys/manager.rs b/kernel/libk/libk-mm/src/phys/manager.rs index 17d7ceef..561633e7 100644 --- a/kernel/libk/libk-mm/src/phys/manager.rs +++ b/kernel/libk/libk-mm/src/phys/manager.rs @@ -7,14 +7,22 @@ use yggdrasil_abi::{error::Error, system::SystemMemoryStats}; use crate::L3_PAGE_SIZE; +#[cfg(any(target_pointer_width = "64", rust_analyzer))] pub type BitmapWord = u64; +#[cfg(any(target_pointer_width = "32", rust_analyzer))] +pub type BitmapWord = u32; pub(super) const BITMAP_WORD_SIZE: usize = BitmapWord::BITS as usize; + +#[cfg(any(target_pointer_width = "64", rust_analyzer))] pub(super) const BITMAP_PAGE_COUNT: usize = 512; +#[cfg(any(target_pointer_width = "32", rust_analyzer))] +pub(super) const BITMAP_PAGE_COUNT: usize = 32; + const HUGE_PAGE_WORD_COUNT: usize = 512 / BITMAP_WORD_SIZE; -pub(super) const TRACKED_PAGE_LIMIT: usize = (BITMAP_PAGE_COUNT * 4096) * 8; +pub(super) const TRACKED_PAGE_LIMIT: usize = (BITMAP_PAGE_COUNT * L3_PAGE_SIZE) * 8; struct MemoryStats { available_pages: AtomicUsize, @@ -28,7 +36,7 @@ static STATS: MemoryStats = MemoryStats { /// Physical memory management interface pub struct PhysicalMemoryManager { - bitmap: PhysicalRefMut<'static, [u64], KernelTableManagerImpl>, + bitmap: PhysicalRefMut<'static, [BitmapWord], KernelTableManagerImpl>, last_free_bit: usize, offset: usize, page_count: usize, @@ -41,7 +49,7 @@ impl PhysicalMemoryManager { page_count: usize, ) -> PhysicalMemoryManager { let bitmap_len = (page_count + (BITMAP_WORD_SIZE - 1)) / BITMAP_WORD_SIZE; - let mut bitmap = PhysicalRefMut::<'static, u64, KernelTableManagerImpl>::map_slice( + let mut bitmap = PhysicalRefMut::<'static, _, KernelTableManagerImpl>::map_slice( bitmap_phys_base, bitmap_len, ); @@ -155,7 +163,7 @@ impl PhysicalMemoryManager { /// /// `addr` must be a page-aligned physical address previously allocated by this implementation. pub unsafe fn free_page(&mut self, page: PhysicalAddress) { - let page = page.into_usize(); + let page = page.try_into_usize().unwrap(); assert!(page >= self.offset); let index = (page - self.offset) / L3_PAGE_SIZE; @@ -171,7 +179,7 @@ impl PhysicalMemoryManager { /// /// Will panic if the address does not point to a valid, reserved (and unallocated) page. pub fn add_available_page(&mut self, page: PhysicalAddress) { - let page = page.into_usize(); + let page = page.try_into_usize().unwrap(); assert!(page >= self.offset); let index = (page - self.offset) / L3_PAGE_SIZE; diff --git a/kernel/libk/libk-mm/src/phys/mod.rs b/kernel/libk/libk-mm/src/phys/mod.rs index 2791fe27..de154981 100644 --- a/kernel/libk/libk-mm/src/phys/mod.rs +++ b/kernel/libk/libk-mm/src/phys/mod.rs @@ -30,7 +30,8 @@ pub struct PhysicalMemoryRegion { } // 8 * 4096 bits per page, 1 page per bit -const MEMORY_UPPER_LIMIT: PhysicalAddress = PhysicalAddress::from_usize(TRACKED_PAGE_LIMIT * 4096); +const MEMORY_UPPER_LIMIT: PhysicalAddress = + PhysicalAddress::from_u64(TRACKED_PAGE_LIMIT as u64 * 4096); /// Global physical memory manager pub static PHYSICAL_MEMORY: OneTimeInit> = @@ -191,12 +192,17 @@ pub unsafe fn init_from_iter< }, ); - if phys_start.into_usize() & (L2_PAGE_SIZE - 1) != 0 { + if phys_start.into_u64() & (L2_PAGE_SIZE as u64 - 1) != 0 { todo!(); } - let mut manager = - PhysicalMemoryManager::new(page_bitmap_phys_base, phys_start.into_usize(), total_count); + let mut manager = PhysicalMemoryManager::new( + page_bitmap_phys_base, + phys_start + .try_into_usize() + .expect("Memory start didn't fit in usize"), + total_count, + ); let mut collected = 0; const MAX_MEMORY: usize = 64 * 1024; diff --git a/kernel/libk/src/module.rs b/kernel/libk/src/module.rs index c6351ee1..383057ed 100644 --- a/kernel/libk/src/module.rs +++ b/kernel/libk/src/module.rs @@ -62,14 +62,14 @@ pub fn load_kernel_symbol_table>( let mut map = DefaultHashTable::new(); loop { - let mut len = [0; 8]; + let mut len = [0; size_of::()]; if symbol_file.read(&mut len)? != len.len() { break; } let len = usize::from_le_bytes(len); symbol_file.read_exact(&mut string_buffer[..len])?; let name = core::str::from_utf8(&string_buffer[..len]).unwrap(); - let mut value = [0; 8]; + let mut value = [0; size_of::()]; symbol_file.read_exact(&mut value)?; let value = usize::from_le_bytes(value); map.insert(name.into(), value); diff --git a/kernel/libk/src/task/binary/elf.rs b/kernel/libk/src/task/binary/elf.rs index 9ba1f522..6fe0b1ba 100644 --- a/kernel/libk/src/task/binary/elf.rs +++ b/kernel/libk/src/task/binary/elf.rs @@ -25,6 +25,16 @@ cfg_if! { const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_X86_64; } else if #[cfg(target_arch = "aarch64")] { const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_AARCH64; + } else if #[cfg(target_arch = "x86")] { + const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_386; + } +} + +cfg_if! { + if #[cfg(target_pointer_width = "64")] { + const EXPECTED_ELF_CLASS: elf::file::Class = elf::file::Class::ELF64; + } else { + const EXPECTED_ELF_CLASS: elf::file::Class = elf::file::Class::ELF32; } } @@ -145,8 +155,8 @@ pub fn open_elf_direct( } // No 32-bit executables currently supported - if elf.ehdr.class != elf::file::Class::ELF64 { - log::warn!("ELF class is not ELF64"); + if elf.ehdr.class != EXPECTED_ELF_CLASS { + log::warn!("ELF class is not {:?}", EXPECTED_ELF_CLASS); return Err(Error::UnrecognizedExecutable); } @@ -462,8 +472,9 @@ fn write_rela(rela: &Rela, space: &ProcessAddressSpace, b: usize) -> Result<(), match width { 8 => { - unsafe { (dst.as_mut_ptr() as *mut u64).write_volatile(value as u64) }; - Ok(()) + todo!(); + // unsafe { (dst.as_mut_ptr() as *mut u64).write_volatile(value as u64) }; + // Ok(()) } _ => todo!("Unhandled relocation width: {}", width), } diff --git a/kernel/libk/src/task/binary/mod.rs b/kernel/libk/src/task/binary/mod.rs index 43eaa7d7..9f0a6100 100644 --- a/kernel/libk/src/task/binary/mod.rs +++ b/kernel/libk/src/task/binary/mod.rs @@ -147,7 +147,16 @@ fn setup_context( virt_stack_base + USER_STACK_PAGES * 0x1000 - TaskContextImpl::USER_STACK_EXTRA_ALIGN; // Fill with some sentinel value to detect stack underflows - let ptr = user_sp as *mut u64; + let mut ptr = user_sp as *mut usize; + + #[cfg(any(target_arch = "x86", rust_analyzer))] + unsafe { + ptr = ptr.sub(1); + ptr.write_foreign_volatile(space, arg); + + ptr = ptr.sub(1); + ptr.write_foreign_volatile(space, 0); + } #[allow(clippy::reversed_empty_ranges)] for i in 0..TaskContextImpl::USER_STACK_EXTRA_ALIGN / 8 { @@ -161,7 +170,7 @@ fn setup_context( TaskContext::user(UserContextInfo { entry: image.entry, argument: arg, - stack_pointer: user_sp, + stack_pointer: ptr.addr(), tls: tls_address, address_space: space.as_address_with_asid(), single_step: false, diff --git a/kernel/libk/src/task/mem.rs b/kernel/libk/src/task/mem.rs index 6dcbb872..9f674ab0 100644 --- a/kernel/libk/src/task/mem.rs +++ b/kernel/libk/src/task/mem.rs @@ -4,8 +4,12 @@ use libk_mm::{address::Virtualize, process::ProcessAddressSpace}; use yggdrasil_abi::error::Error; // XXX +#[cfg(any(target_pointer_width = "64", rust_analyzer))] const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; +#[cfg(any(target_pointer_width = "32", rust_analyzer))] +const KERNEL_VIRT_OFFSET: usize = 0xC0000000; + /// Helper trait to allow cross-address space access to pointers pub trait ForeignPointer: Sized { /// Perform a volatile pointer write without dropping the old value. diff --git a/kernel/libk/src/task/types.rs b/kernel/libk/src/task/types.rs index f07d62e5..e9179e69 100644 --- a/kernel/libk/src/task/types.rs +++ b/kernel/libk/src/task/types.rs @@ -153,7 +153,7 @@ impl AllocateProcessId for ProcessId { } } -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", rust_analyzer))] impl ProcessTlsLayout { /// Constructs a new thread-local storage layout info struct pub fn new(align: usize, data_size: usize, mem_size: usize) -> Self { @@ -174,7 +174,7 @@ impl ProcessTlsLayout { } } -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "x86_64", rust_analyzer))] impl ProcessTlsLayout { /// Constructs a new thread-local storage layout info struct pub fn new(align: usize, data_size: usize, mem_size: usize) -> Self { @@ -198,3 +198,10 @@ impl ProcessTlsLayout { } } } + +#[cfg(any(target_arch = "x86", rust_analyzer))] +impl ProcessTlsLayout { + pub fn new(align: usize, data_size: usize, mem_size: usize) -> Self { + todo!() + } +} diff --git a/kernel/libk/src/vfs/terminal.rs b/kernel/libk/src/vfs/terminal.rs index 1c1de468..87e2e89b 100644 --- a/kernel/libk/src/vfs/terminal.rs +++ b/kernel/libk/src/vfs/terminal.rs @@ -11,7 +11,10 @@ use libk_util::{ }; use yggdrasil_abi::{ error::Error, - io::{DeviceRequest, TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalSize}, + io::{ + DeviceRequest, KeyboardKey, KeyboardKeyEvent, TerminalInputOptions, TerminalLineOptions, + TerminalOptions, TerminalSize, + }, process::{ProcessGroupId, Signal}, }; @@ -200,6 +203,50 @@ impl Terminal { _ => Err(Error::InvalidOperation), } } + + pub async fn consume_keyboard(&'static self, keyboard: &'static D) { + let mut buf = [0; 4]; + let mut lshift = false; + let mut rshift = false; + let mut lctrl = false; + let mut rctrl = false; + + loop { + keyboard.read(&mut buf).await.unwrap(); + let (key, pressed) = KeyboardKeyEvent::from_bytes(buf).split(); + let ctrl = lctrl || rctrl; + let shift = lshift || rshift; + + match key { + KeyboardKey::LShift => lshift = pressed, + KeyboardKey::RShift => rshift = pressed, + KeyboardKey::LControl => lctrl = pressed, + KeyboardKey::RControl => rctrl = pressed, + KeyboardKey::Char(ch) if ctrl => match ch { + b'c' => self.write_to_input(self.config().chars.interrupt), + b'd' => self.write_to_input(self.config().chars.eof), + _ => (), + }, + KeyboardKey::Char(ch) if shift => match ch { + ch if ch.is_ascii_lowercase() => { + self.write_to_input(ch.to_ascii_uppercase()); + } + _ => (), + }, + KeyboardKey::Char(ch) if pressed => { + // TODO shift/ctrl + self.write_to_input(ch); + } + KeyboardKey::Enter if pressed => { + self.write_to_input(b'\n'); + } + KeyboardKey::Backspace if pressed => { + self.write_to_input(b'\x7F'); + } + _ => (), + } + } + } } impl TerminalInput { diff --git a/kernel/modules/test_mod/Cargo.lock b/kernel/modules/test_mod/Cargo.lock index d7212c16..3a047efb 100644 --- a/kernel/modules/test_mod/Cargo.lock +++ b/kernel/modules/test_mod/Cargo.lock @@ -226,6 +226,7 @@ dependencies = [ "cfg-if", "kernel-arch-aarch64", "kernel-arch-hosted", + "kernel-arch-i686", "kernel-arch-interface", "kernel-arch-x86_64", ] @@ -254,6 +255,20 @@ dependencies = [ "yggdrasil-abi", ] +[[package]] +name = "kernel-arch-i686" +version = "0.1.0" +dependencies = [ + "bitflags", + "device-api", + "kernel-arch-interface", + "libk-mm-interface", + "memtables", + "static_assertions", + "tock-registers", + "yggdrasil-abi", +] + [[package]] name = "kernel-arch-interface" version = "0.1.0" diff --git a/kernel/src/arch/i686/boot/mod.rs b/kernel/src/arch/i686/boot/mod.rs new file mode 100644 index 00000000..9f642e95 --- /dev/null +++ b/kernel/src/arch/i686/boot/mod.rs @@ -0,0 +1,148 @@ +use core::arch::global_asm; + +use bytemuck::{Pod, Zeroable}; +use kernel_arch_i686::{ + mem::KERNEL_TABLES, + registers::{CR0, CR4}, +}; +use kernel_fs::devfs; +use libk::task::runtime; +use libk_mm::{ + address::{PhysicalAddress, Virtualize}, + phys::PhysicalMemoryRegion, + pointer::PhysicalRef, +}; +use tock_registers::interfaces::ReadWriteable; + +use crate::{arch::x86::gdt, kernel_main, mem::KERNEL_VIRT_OFFSET}; + +use super::{exception, I686, PLATFORM}; + +const BOOT_STACK_SIZE: usize = 64 * 1024; + +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +#[repr(C)] +pub struct MultibootInfo { + pub flags: u32, + pub mem_lower: u32, + pub mem_upper: u32, + pub boot_device: u32, + pub cmdline: u32, + pub mods_count: u32, + pub mods_addr: u32, + pub syms: [u32; 4], + pub mmap_length: u32, + pub mmap_addr: u32, + // ... +} + +#[derive(Clone)] +pub struct MultibootMemoryMapIter<'a> { + multiboot_info: &'a MultibootInfo, + base: usize, + pos: usize, +} + +#[derive(Clone)] +pub struct MultibootModuleIter<'a> { + multiboot_info: &'a MultibootInfo, + base: usize, + pos: usize, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C, packed)] +pub struct MultibootMemoryMapEntry { + pub size: u32, + pub addr: u64, + pub len: u64, + pub ty: u32, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MultibootModuleEntry { + pub mod_start: u32, + pub mod_end: u32, + pub cmdline: u32, + _pad: u32, +} + +impl<'a> Iterator for MultibootMemoryMapIter<'a> { + type Item = PhysicalMemoryRegion; + + fn next(&mut self) -> Option { + loop { + if self.pos + size_of::() + >= self.multiboot_info.mmap_length as usize + { + return None; + } + + let entry: &MultibootMemoryMapEntry = + unsafe { core::mem::transmute(self.base + self.pos) }; + + self.pos += entry.size as usize + size_of::(); + + if !entry.is_available() { + continue; + } + + return Some(PhysicalMemoryRegion { + base: PhysicalAddress::from_u64(entry.addr), + size: entry.len.try_into().unwrap(), + }); + } + } +} + +impl MultibootInfo { + pub fn memory_map_iter(&self) -> MultibootMemoryMapIter { + MultibootMemoryMapIter { + multiboot_info: self, + base: PhysicalAddress::from_u32(self.mmap_addr).virtualize(), + pos: 0, + } + } + + pub fn modules(&self) -> &[MultibootModuleEntry] { + let base = PhysicalAddress::from_u32(self.mods_addr).virtualize(); + unsafe { core::slice::from_raw_parts(base as *const _, self.mods_count as usize) } + } +} + +impl MultibootMemoryMapEntry { + pub fn is_available(&self) -> bool { + self.ty == 1 + } +} + +#[repr(C, align(0x20))] +struct BootStack { + data: [u8; BOOT_STACK_SIZE], +} + +#[link_section = ".bss"] +static mut BOOT_STACK: BootStack = BootStack { + data: [0; BOOT_STACK_SIZE], +}; + +unsafe extern "C" fn __i686_upper_entry(ebx: u32) -> ! { + kernel_arch_i686::mem::init_fixed_tables(); + + let multiboot_info = PhysicalRef::::map(PhysicalAddress::from_u32(ebx)); + // TODO split memory and platform init + PLATFORM.init_platform(&multiboot_info).ok(); + + kernel_main() +} + +global_asm!( + include_str!("multiboot.S"), + tables = sym KERNEL_TABLES, + entry = sym __i686_upper_entry, + boot_stack_bottom = sym BOOT_STACK, + boot_stack_size = const BOOT_STACK_SIZE, + kernel_virt_offset = const KERNEL_VIRT_OFFSET, + options(att_syntax) +); diff --git a/kernel/src/arch/i686/boot/multiboot.S b/kernel/src/arch/i686/boot/multiboot.S new file mode 100644 index 00000000..3fe06ca5 --- /dev/null +++ b/kernel/src/arch/i686/boot/multiboot.S @@ -0,0 +1,56 @@ +.set MB_MAGIC, 0x1BADB002 +.set MB_FLAGS, (1 << 1) + +.set MB_CHECKSUM, 0xFFFFFFFF & (-(MB_MAGIC + MB_FLAGS)) + + +.section .multiboot +.long MB_MAGIC +.long MB_FLAGS +.long MB_CHECKSUM + +.section .text.entry + +.set KERNEL_SPLIT_L0_INDEX, {kernel_virt_offset} >> 22 +.set TABLE_SPLIT_OFFSET, KERNEL_SPLIT_L0_INDEX * 4 + +.p2align 4 +.global __i686_entry +__i686_entry: + // TODO check %eax for 2badb002 + // %ebx - multiboot info + + // Setup early lower 768MiB mapping + mov ${tables} - {kernel_virt_offset}, %edi + mov $0xC0, %ecx +1: + dec %ecx + + mov %ecx, %eax + shl $22, %eax + or $(1 << 7) | (1 << 1) | (1 << 0), %eax + + // Lower half + mov %eax, (%edi, %ecx, 4) + // Upper half + mov %eax, TABLE_SPLIT_OFFSET(%edi, %ecx, 4) + + test %ecx, %ecx + jnz 1b + + mov %edi, %cr3 + + // Enable PSE + mov %cr4, %eax + or $(1 << 4), %eax + mov %eax, %cr4 + + // Enable PG + mov %cr0, %eax + or $(1 << 31), %eax + mov %eax, %cr0 + + // Enter the kernel + mov ${boot_stack_bottom} + {boot_stack_size}, %esp + push %ebx + call {entry} diff --git a/kernel/src/arch/i686/exception.rs b/kernel/src/arch/i686/exception.rs new file mode 100644 index 00000000..0ccd8e79 --- /dev/null +++ b/kernel/src/arch/i686/exception.rs @@ -0,0 +1,272 @@ +use core::{arch::global_asm, ptr::addr_of}; + +use abi::{primitive_enum, process::Signal}; +use kernel_arch_i686::context::{ExceptionFrame, SyscallFrame}; +use libk::task::thread::Thread; +use static_assertions::const_assert_eq; + +use crate::{arch::x86::peripherals::i8259, syscall}; + +primitive_enum! { + enum ExceptionKind: u32 { + DivisionError = 0, + Debug = 1, + NonMaskableInterrupt = 2, + Breakpoint = 3, + Overflow = 4, + BoundRangeExceeded = 5, + InvalidOpcode = 6, + DeviceNotAvailable = 7, + DoubleFault = 8, + InvalidTss = 10, + SegmentNotPresent = 11, + StackSegmentFault = 12, + GeneralProtectionFault = 13, + PageFault = 14, + FpuException = 16, + AlignmentCheck = 17, + MachineCheck = 18, + SimdFpuException = 19, + VirtualizationException = 20, + ControlProtectionException = 21, + + Unknown = 99, + } +} + +impl ExceptionKind { + fn ring3_possible(&self) -> bool { + matches!( + self, + Self::DivisionError + | Self::Debug + | Self::Breakpoint + | Self::Overflow + | Self::BoundRangeExceeded + | Self::InvalidOpcode + | Self::GeneralProtectionFault + | Self::PageFault + | Self::FpuException + | Self::AlignmentCheck + | Self::SimdFpuException + ) + } +} + +/// Exception table entry +#[cfg(any(target_arch = "x86_64", rust_analyzer))] +#[allow(dead_code)] +#[derive(Clone, Copy)] +#[repr(packed)] +pub struct Entry { + base_lo: u16, + selector: u16, + __res0: u8, + flags: u8, + base_hi: u16, + base_ex: u32, + __res1: u32, +} + +#[cfg(any(target_arch = "x86", rust_analyzer))] +#[allow(dead_code)] +#[derive(Clone, Copy)] +#[repr(C, align(8))] +pub struct Entry { + base_lo: u16, + selector: u16, + __res0: u8, + flags: u8, + base_hi: u16, +} + +#[allow(dead_code)] +#[repr(packed)] +struct Pointer { + limit: u16, + offset: usize, +} + +const SIZE: usize = 256; + +impl Entry { + /// Entry is valid + pub const PRESENT: u8 = 1 << 7; + /// Entry is a 32-bit interrupt + pub const INT32: u8 = 0xE; + + pub const DPL3: u8 = 3 << 5; +} + +#[cfg(any(target_arch = "x86", rust_analyzer))] +impl Entry { + pub const NULL: Self = Self { + base_lo: 0, + base_hi: 0, + selector: 0, + flags: 0, + __res0: 0, + }; + + pub const fn new(base: usize, selector: u16, flags: u8) -> Self { + Self { + base_lo: (base & 0xFFFF) as u16, + base_hi: (base >> 16) as u16, + selector, + flags, + __res0: 0, + } + } +} + +#[cfg(target_arch = "x86_64")] +impl Entry { + /// Empty entry + pub const NULL: Self = Self { + base_lo: 0, + base_hi: 0, + base_ex: 0, + selector: 0, + flags: 0, + __res0: 0, + __res1: 0, + }; + + /// Constructs an interrupt table entry + pub const fn new(base: usize, selector: u16, flags: u8) -> Self { + Self { + base_lo: (base & 0xFFFF) as u16, + base_hi: ((base >> 16) & 0xFFFF) as u16, + base_ex: (base >> 32) as u32, + selector, + flags, + __res0: 0, + __res1: 0, + } + } +} + +static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE]; + +fn kernel_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) -> ! { + let cr3: usize; + let cr2: usize; + unsafe { + core::arch::asm!("mov %cr3, {0}", out(reg) cr3, options(att_syntax)); + core::arch::asm!("mov %cr2, {0}", out(reg) cr2, options(att_syntax)); + } + + fatalln!("{:?} in KERNEL, frame {:p}, cr3 = {:#x}", kind, frame, cr3); + if kind == ExceptionKind::PageFault { + fatalln!("cr2 = {:#x}", cr2); + } + fatalln!("CS:EIP = {:02x}:{:08x}", frame.cs, frame.eip); + + panic!("Irrecoverable exception") +} + +fn user_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) { + let thread = Thread::current(); + let cr3: usize; + unsafe { + core::arch::asm!("mov %cr3, {0}", out(reg) cr3, options(att_syntax)); + } + + warnln!("{:?} in {} {:?}", kind, thread.id, thread.name); + warnln!("CR3 = {:#x}", cr3); + + match kind { + ExceptionKind::PageFault => { + let cr2: usize; + unsafe { + core::arch::asm!("mov %cr2, {0}", out(reg) cr2, options(att_syntax)); + } + warnln!("CR2 = {:#x}", cr2); + + thread.raise_signal(Signal::MemoryAccessViolation); + } + ExceptionKind::GeneralProtectionFault => { + thread.raise_signal(Signal::MemoryAccessViolation); + } + ExceptionKind::InvalidOpcode => { + thread.raise_signal(Signal::Aborted); + } + _ => { + todo!() + } + } +} + +extern "C" fn __i686_exception_handler(frame: *mut ExceptionFrame) { + let frame = unsafe { &mut *frame }; + let kind = ExceptionKind::try_from(frame.exc_number).unwrap_or(ExceptionKind::Unknown); + + if kind.ring3_possible() && frame.cs == 0x1B { + user_exception_inner(kind, frame); + + unsafe { + Thread::current().handle_pending_signals(frame); + } + } else { + kernel_exception_inner(kind, frame); + } +} + +extern "C" fn __i686_syscall_handler(frame: *mut SyscallFrame) { + let frame = unsafe { &mut *frame }; + // TODO check that cs == 0x1B, kernel to kernel syscalls disallowed + let result = syscall::raw_syscall_handler(frame.eax, &frame.args); + + if result & (1 << 63) != 0 { + assert_eq!(result & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000); + } + + frame.eax = result as _; + + if let Some(thread) = Thread::get_current() { + unsafe { thread.handle_pending_signals(frame) }; + } +} + +pub unsafe fn init_exceptions() { + extern "C" { + static __i686_exception_vectors: [usize; 32]; + + fn __i686_dummy_vector(); + fn __i686_syscall_vector(); + } + + for (i, &entry) in __i686_exception_vectors.iter().enumerate() { + IDT[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32); + } + + for i in 32..256 { + IDT[i] = Entry::new( + __i686_dummy_vector as usize, + 0x08, + Entry::PRESENT | Entry::INT32 | Entry::DPL3, + ); + } + + i8259::setup_vectors(&mut IDT[32..]); + + IDT[0x80] = Entry::new( + __i686_syscall_vector as usize, + 0x08, + Entry::PRESENT | Entry::INT32 | Entry::DPL3, + ); + + let idtr = Pointer { + limit: (IDT.len() * size_of::()) as u16 - 1, + offset: addr_of!(IDT) as usize, + }; + + core::arch::asm!("wbinvd; lidt ({0})", in(reg) &idtr, options(att_syntax)); +} + +global_asm!( + include_str!("vectors.S"), + exception_handler = sym __i686_exception_handler, + syscall_handler = sym __i686_syscall_handler, + options(att_syntax) +); diff --git a/kernel/src/arch/i686/mod.rs b/kernel/src/arch/i686/mod.rs new file mode 100644 index 00000000..b35b123b --- /dev/null +++ b/kernel/src/arch/i686/mod.rs @@ -0,0 +1,248 @@ +#![allow(missing_docs)] +use abi::{ + error::Error, + io::{KeyboardKey, KeyboardKeyEvent}, +}; +use boot::{MultibootInfo, MultibootMemoryMapEntry}; +use device_api::{ + interrupt::{IpiDeliveryTarget, IpiMessage, Irq}, + Device, ResetDevice, +}; +use git_version::git_version; +use kernel_arch_i686::{mem::table::L3, PerCpuData}; +use kernel_fs::devfs::{self, CharDeviceType}; +use libk::{ + arch::Cpu, + task::runtime, + vfs::{CharDevice, Terminal, TerminalInput}, +}; +use libk_device::{register_external_interrupt_controller, register_monotonic_timestamp_provider}; +use libk_mm::{ + address::{PhysicalAddress, Virtualize}, + phys::{self, reserved::reserve_region, PhysicalMemoryRegion}, + table::EntryLevelExt, +}; +use libk_util::{sync::IrqSafeSpinlock, OneTimeInit}; +use peripherals::textfb::TextFramebuffer; +use ygg_driver_input::KEYBOARD_DEVICE; +use ygg_driver_pci::{LegacyPciAccess, PciBusManager}; + +pub mod boot; +pub mod exception; +pub mod peripherals; + +use crate::{ + arch::x86::{ + gdt, + intrinsics::IoPortAccess, + peripherals::{i8253::I8253, ps2::PS2}, + }, + debug::{self, LogLevel}, + device::{ + self, + display::console::{add_console_autoflush, ConsoleWrapper, DisplayConsole}, + }, + fs::{Initrd, INITRD_DATA}, +}; + +use super::{ + x86::{ + intrinsics::IoPort, + peripherals::{i8259::I8259, serial::ComPort}, + }, + Platform, +}; + +struct LegacyPciInner { + address: IoPort, + data: IoPort, +} + +struct LegacyPci { + inner: IrqSafeSpinlock, +} + +pub struct I686 { + com1_3: OneTimeInit, + textfb: OneTimeInit, + textfb_console: OneTimeInit>>, +} + +static PCI: LegacyPci = LegacyPci { + inner: IrqSafeSpinlock::new(LegacyPciInner { + address: IoPort::new(0xCF8), + data: IoPort::new(0xCFC), + }), +}; + +pub static PLATFORM: I686 = I686 { + com1_3: OneTimeInit::new(), + textfb: OneTimeInit::new(), + textfb_console: OneTimeInit::new(), +}; + +impl Platform for I686 { + const KERNEL_VIRT_OFFSET: usize = 0xC0000000; + type L3 = L3; + + unsafe fn reset(&self) -> ! { + loop {} + } + + unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error> { + Ok(()) + } + + unsafe fn start_application_processors(&self) { + // No APs in i686, go get a better CPU + } + + fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error> { + loop {} + } +} + +impl I686 { + fn init_initrd(initrd_start: PhysicalAddress, initrd_end: PhysicalAddress) { + if initrd_start.is_zero() || initrd_end <= initrd_start { + infoln!("No initrd loaded"); + return; + } + + let start_aligned = initrd_start.page_align_down::(); + let end_aligned = initrd_start.page_align_up::(); + + let data = unsafe { + core::slice::from_raw_parts( + start_aligned.virtualize() as *const u8, + initrd_end - initrd_start, + ) + }; + + let initrd = Initrd { + phys_page_start: start_aligned, + phys_page_len: end_aligned - start_aligned, + data, + }; + + INITRD_DATA.init(initrd); + } + + unsafe fn init_platform(&'static self, multiboot_info: &MultibootInfo) -> Result<(), Error> { + // Init serial output + let com1_3 = self + .com1_3 + .init(ComPort::new(0x3F8, 0x3E8, Irq::External(4))); + debug::add_sink(com1_3.port_a(), LogLevel::Debug); + + debug::init(); + + reserve_region( + "lowmem", + PhysicalMemoryRegion { + base: PhysicalAddress::ZERO, + size: 1024 * 1024, + }, + ); + + let modules = multiboot_info.modules(); + if !modules.is_empty() { + let initrd = &modules[0]; + let start = PhysicalAddress::from_u32(initrd.mod_start); + let end = PhysicalAddress::from_u32(initrd.mod_end); + + if initrd.mod_start < initrd.mod_end { + let size = initrd.mod_end - initrd.mod_start; + + reserve_region( + "initrd", + PhysicalMemoryRegion { + base: start, + size: size as _, + }, + ); + } + + Self::init_initrd(start, end); + } + + // Initialize physical memory + phys::init_from_iter(multiboot_info.memory_map_iter(), |_, _, _| Ok(()))?; + + unsafe { + gdt::init(); + exception::init_exceptions(); + } + + let cpu_data = PerCpuData {}; + Cpu::init_local(Some(0), cpu_data); + + runtime::init_task_queue(); + devfs::init(); + + I8259.init().expect("Could not initialize i8259 PIC"); + register_external_interrupt_controller(&I8259); + I8253.init().expect("Could not initialize i8253 Timer"); + I8253.init_irq().expect("Could not setup timer IRQ"); + register_monotonic_timestamp_provider(&I8253); + PS2.init()?; + PS2.init_irq()?; + + // Setup text framebuffer + // TODO check if video mode is set from boot info + let textfb = TextFramebuffer::new(PhysicalAddress::from_u32(0xB8000), 80, 25)?; + let textfb = self.textfb.init(textfb); + debug::add_sink(textfb, LogLevel::Info); + add_console_autoflush(textfb); + + let textfb_console = Terminal::from_parts( + Default::default(), + TerminalInput::with_capacity(256).unwrap(), + ConsoleWrapper(textfb), + ); + let textfb_console = self.textfb_console.init(textfb_console); + + runtime::spawn(textfb_console.consume_keyboard(&KEYBOARD_DEVICE)).ok(); + devfs::add_char_device(textfb_console, CharDeviceType::TtyRegular).ok(); + + device::register_device(&PS2); + + infoln!( + "Yggdrasil v{} ({})", + env!("CARGO_PKG_VERSION"), + git_version!() + ); + + PciBusManager::add_legacy_segment(&PCI)?; + PciBusManager::setup_bus_devices()?; + + Ok(()) + } +} + +impl LegacyPciAccess for LegacyPci { + fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) { + assert_eq!(offset & 0x3, 0); + let inner = self.inner.lock(); + inner.address.write(Self::addr(bus, slot, func, offset)); + inner.data.write(value); + } + + fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 { + assert_eq!(offset & 0x3, 0); + let inner = self.inner.lock(); + inner.address.write(Self::addr(bus, slot, func, offset)); + inner.data.read() + } +} + +impl LegacyPci { + #[inline] + const fn addr(bus: u8, slot: u8, func: u8, offset: u8) -> u32 { + ((bus as u32) << 16) + | ((slot as u32) << 11) + | ((func as u32) << 8) + | (offset as u32 & 0xFC) + | (1 << 31) + } +} diff --git a/kernel/src/arch/i686/peripherals/mod.rs b/kernel/src/arch/i686/peripherals/mod.rs new file mode 100644 index 00000000..44bf4130 --- /dev/null +++ b/kernel/src/arch/i686/peripherals/mod.rs @@ -0,0 +1 @@ +pub mod textfb; diff --git a/kernel/src/arch/i686/peripherals/textfb.rs b/kernel/src/arch/i686/peripherals/textfb.rs new file mode 100644 index 00000000..8cf5ac48 --- /dev/null +++ b/kernel/src/arch/i686/peripherals/textfb.rs @@ -0,0 +1,152 @@ +use abi::error::Error; +use libk_mm::{ + address::PhysicalAddress, + device::{DeviceMemoryAttributes, DeviceMemoryIoMut}, +}; +use libk_util::sync::IrqSafeSpinlock; + +use crate::{ + arch::x86::intrinsics::{io_wait, IoPort, IoPortAccess}, + debug::DebugSink, + device::display::console::{ + Attributes, ColorAttribute, ConsoleBuffer, ConsoleChar, ConsoleState, DisplayConsole, + }, +}; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct Character { + pub char: u8, + pub attr: u8, +} + +struct Cursor { + port0: IoPort, + port1: IoPort, + + last_row: u8, + last_col: u8, +} + +struct Inner { + data: DeviceMemoryIoMut<'static, [Character]>, + cursor: Cursor, +} + +pub struct TextFramebuffer { + state: IrqSafeSpinlock, + inner: IrqSafeSpinlock, + width: usize, + height: usize, +} + +impl Cursor { + fn set_position(&mut self, row: u8, col: u8) { + if self.last_row != row || self.last_col != col { + let pos = row as u16 * 80 + col as u16; + + self.port0.write(0x0F); + self.port1.write(pos as u8); + io_wait(); + self.port0.write(0x0E); + self.port1.write((pos >> 8) as u8); + io_wait(); + + self.last_row = row; + self.last_col = col; + } + } +} + +impl TextFramebuffer { + pub fn new(base: PhysicalAddress, width: usize, height: usize) -> Result { + let data = unsafe { + DeviceMemoryIoMut::map_slice(base, width * height, DeviceMemoryAttributes::default()) + }?; + let cursor = Cursor { + port0: IoPort::new(0x3D4), + port1: IoPort::new(0x3D5), + + last_row: 0, + last_col: 0, + }; + let state = ConsoleState::new(ConsoleBuffer::new(height as _)?); + + Ok(Self { + inner: IrqSafeSpinlock::new(Inner { data, cursor }), + state: IrqSafeSpinlock::new(state), + width, + height, + }) + } +} + +impl DebugSink for TextFramebuffer { + fn putc(&self, c: u8) -> Result<(), Error> { + self.write_char(c); + Ok(()) + } + + fn supports_control_sequences(&self) -> bool { + true + } +} + +impl DisplayConsole for TextFramebuffer { + fn state(&self) -> &IrqSafeSpinlock { + &self.state + } + + fn flush(&self, state: &mut ConsoleState) { + let mut inner = self.inner.lock(); + let mut iter = state.buffer.flush_rows(); + + while let Some((row_idx, row)) = iter.next_dirty() { + let row_idx = row_idx as usize; + let row_start = self.width * row_idx; + + for (i, ch) in row.iter().take(self.width).enumerate() { + let attr = convert_attrs(ch); + // TODO attributes + inner.data[row_start + i] = Character { + char: ch.character(), + attr, + }; + } + } + + inner + .cursor + .set_position(state.cursor_row as _, state.cursor_col as _); + } + + fn text_dimensions(&self) -> (usize, usize) { + (self.width, self.height) + } +} + +fn convert_attrs(ch: &ConsoleChar) -> u8 { + let (fg, bg, attr) = ch.attributes(); + let bg = convert_color(bg); + let mut fg = convert_color(fg); + if ch.character() == b' ' { + fg = 7; + } + if attr.contains(Attributes::BOLD) { + fg += 8; + } + (fg | (bg << 4)) +} + +fn convert_color(c: ColorAttribute) -> u8 { + match c { + ColorAttribute::Black => 0, + ColorAttribute::Blue => 1, + ColorAttribute::Green => 2, + ColorAttribute::Cyan => 3, + ColorAttribute::Red => 4, + ColorAttribute::Magenta => 5, + ColorAttribute::Yellow => 6, + ColorAttribute::White => 7, + } +} diff --git a/kernel/src/arch/i686/vectors.S b/kernel/src/arch/i686/vectors.S new file mode 100644 index 00000000..f281897e --- /dev/null +++ b/kernel/src/arch/i686/vectors.S @@ -0,0 +1,173 @@ +// vi: ft=asm : + +.macro EXC_SAVE_STATE + push %edi + push %esi + push %ebp + push %ebx + push %edx + push %ecx + push %eax +.endm + +.macro EXC_RESTORE_STATE + pop %eax + pop %ecx + pop %edx + pop %ebx + pop %ebp + pop %esi + pop %edi +.endm + +.macro ISR_NERR, n +__i686_exc_\n: + cli + pushl $0 + pushl $\n + jmp __i686_exc_common +.endm + +.macro ISR_YERR, n +__i686_exc_\n: + cli + pushl $\n + jmp __i686_exc_common +.endm + +.global __i686_dummy_vector +.global __i686_syscall_vector + +.section .text +__i686_exc_common: + // %esp + 0: error number + // %esp + 4: error code (or 0) + // %esp + 8: %eip + // %esp + 12: %cs + // %esp + 16: %eflags + // If %cs != 0x08 + // %esp + 20: %esp + // %esp + 24: %ss + + EXC_SAVE_STATE + + mov %esp, %ebp + push %ebp + + call {exception_handler} + + mov %ebp, %esp + + EXC_RESTORE_STATE + + // Remove error code/number from the stack + add $8, %esp + + iret + +__i686_dummy_vector: + jmp . + +__i686_syscall_vector: + // %esp + 0: %eip + // %esp + 4: %cs + // %esp + 8: %eflags + // %esp + 12: %esp + // %esp + 16: %ss + + pushl %ebp + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + + mov %esp, %ebp + push %ebp + + call {syscall_handler} + + mov %ebp, %esp + + pop %eax + pop %ebx + pop %ecx + pop %edx + pop %esi + pop %edi + pop %ebp + + iret + +ISR_NERR 0 +ISR_NERR 1 +ISR_NERR 2 +ISR_NERR 3 +ISR_NERR 4 +ISR_NERR 5 +ISR_NERR 6 +ISR_NERR 7 +ISR_YERR 8 +ISR_NERR 9 +ISR_YERR 10 +ISR_YERR 11 +ISR_YERR 12 +ISR_YERR 13 +ISR_YERR 14 +ISR_NERR 15 +ISR_NERR 16 +ISR_YERR 17 +ISR_NERR 18 +ISR_NERR 19 +ISR_NERR 20 +ISR_NERR 21 +ISR_NERR 22 +ISR_NERR 23 +ISR_NERR 24 +ISR_NERR 25 +ISR_NERR 26 +ISR_NERR 27 +ISR_NERR 28 +ISR_NERR 29 +ISR_YERR 30 +ISR_NERR 31 + +.section .rodata +.global __i686_exception_vectors +.p2align 4 +__i686_exception_vectors: + .long __i686_exc_0 + .long __i686_exc_1 + .long __i686_exc_2 + .long __i686_exc_3 + .long __i686_exc_4 + .long __i686_exc_5 + .long __i686_exc_6 + .long __i686_exc_7 + .long __i686_exc_8 + .long __i686_exc_9 + .long __i686_exc_10 + .long __i686_exc_11 + .long __i686_exc_12 + .long __i686_exc_13 + .long __i686_exc_14 + .long __i686_exc_15 + .long __i686_exc_16 + .long __i686_exc_17 + .long __i686_exc_18 + .long __i686_exc_19 + .long __i686_exc_20 + .long __i686_exc_21 + .long __i686_exc_22 + .long __i686_exc_23 + .long __i686_exc_24 + .long __i686_exc_25 + .long __i686_exc_26 + .long __i686_exc_27 + .long __i686_exc_28 + .long __i686_exc_29 + .long __i686_exc_30 + .long __i686_exc_31 + +.section .text diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs index 4f37a5ac..324cae60 100644 --- a/kernel/src/arch/mod.rs +++ b/kernel/src/arch/mod.rs @@ -14,12 +14,20 @@ pub mod aarch64; #[cfg(any(target_arch = "aarch64", rust_analyzer))] pub use aarch64::{AArch64 as PlatformImpl, PLATFORM}; +#[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))] +pub mod x86; + #[cfg(any(target_arch = "x86_64", rust_analyzer))] pub mod x86_64; #[cfg(any(target_arch = "x86_64", rust_analyzer))] pub use x86_64::{PLATFORM, X86_64 as PlatformImpl}; -#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] +#[cfg(any(target_arch = "x86", rust_analyzer))] +pub mod i686; +#[cfg(any(target_arch = "x86", rust_analyzer))] +pub use i686::{I686 as PlatformImpl, PLATFORM}; + +#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))] compile_error!("Unsupported architecture"); /// Architecture-specific lowest level of page mapping diff --git a/kernel/src/arch/x86/gdt.rs b/kernel/src/arch/x86/gdt.rs new file mode 100644 index 00000000..ee232f08 --- /dev/null +++ b/kernel/src/arch/x86/gdt.rs @@ -0,0 +1,313 @@ +use core::mem::size_of; + +use alloc::boxed::Box; + +#[allow(dead_code)] +#[repr(packed)] +struct Entry { + limit_lo: u16, + base_lo: u16, + base_mi: u8, + access: u8, + flags: u8, + base_hi: u8, +} + +#[allow(dead_code)] +#[repr(packed)] +struct Pointer { + limit: u16, + offset: usize, +} + +impl Entry { + const ACC_PRESENT: u8 = 1 << 7; + const ACC_SYSTEM: u8 = 1 << 4; + const ACC_EXECUTE: u8 = 1 << 3; + const ACC_WRITE: u8 = 1 << 1; + + #[allow(unused)] + const ACC_RING3: u8 = 3 << 5; + #[allow(unused)] + const ACC_ACCESS: u8 = 1 << 0; + + const FLAG_4K: u8 = 1 << 7; + const FLAG_32: u8 = 1 << 6; + + const NULL: Self = Self { + base_lo: 0, + base_mi: 0, + base_hi: 0, + access: 0, + flags: 0, + limit_lo: 0, + }; + + const fn new(base: u32, limit: u32, flags: u8, access: u8) -> Self { + Self { + base_lo: (base & 0xFFFF) as u16, + base_mi: ((base >> 16) & 0xFF) as u8, + base_hi: ((base >> 24) & 0xFF) as u8, + access, + flags: (flags & 0xF0) | (((limit >> 16) & 0xF) as u8), + limit_lo: (limit & 0xFFFF) as u16, + } + } +} + +#[cfg(any(target_arch = "x86", rust_analyzer))] +mod imp { + use alloc::boxed::Box; + + use super::{Entry, Pointer}; + + pub use kernel_arch_i686::gdt::{Tss, TSS}; + + impl Entry { + const RING0_CS32: Entry = Entry::new( + 0, + 0xFFFFF, + Entry::FLAG_4K | Entry::FLAG_32, + Entry::ACC_ACCESS | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE | Entry::ACC_PRESENT, + ); + const RING0_DS32: Entry = Entry::new( + 0, + 0xFFFFF, + Entry::FLAG_4K | Entry::FLAG_32, + Entry::ACC_ACCESS | Entry::ACC_SYSTEM | Entry::ACC_WRITE | Entry::ACC_PRESENT, + ); + const RING3_CS32: Entry = Entry::new( + 0, + 0xFFFFF, + Entry::FLAG_4K | Entry::FLAG_32, + Entry::ACC_SYSTEM | Entry::ACC_EXECUTE | Entry::ACC_PRESENT | Entry::ACC_RING3, + ); + const RING3_DS32: Entry = Entry::new( + 0, + 0xFFFFF, + Entry::FLAG_4K | Entry::FLAG_32, + Entry::ACC_SYSTEM | Entry::ACC_WRITE | Entry::ACC_PRESENT | Entry::ACC_RING3, + ); + + const fn tss(base: u32, limit: u32) -> Self { + Self::new( + base, + limit, + 0, + Entry::ACC_ACCESS | Entry::ACC_PRESENT | Entry::ACC_EXECUTE, + ) + } + } + + pub fn create_gdt() -> (&'static [Entry], &'static Tss) { + // Won't be deallocated, so leaks are not a concern + let tss = unsafe { &mut TSS }; + tss.ss0 = 0x10; + let tss_addr = (tss as *mut Tss).addr(); + let mut gdt = Box::new([ + Entry::NULL, + Entry::RING0_CS32, + Entry::RING0_DS32, + Entry::RING3_CS32, + Entry::RING3_DS32, + Entry::tss(tss_addr as u32, (size_of::() - 1) as u32), + ]); + + (Box::leak(gdt), tss) + } + + pub unsafe fn load_gdt(gdt: &'static [Entry]) { + let gdt_addr = gdt.as_ptr().addr(); + let gdtr = Pointer { + limit: (gdt.len() * size_of::()) as u16 - 1, + offset: gdt_addr, + }; + + core::arch::asm!( + r#" + wbinvd + lgdt ({0}) + + ljmpl $0x08, $1f +1: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + mov $0x28, %ax + ltr %ax + "#, + in(reg) &gdtr, + out("eax") _, + out("ecx") _, + options(att_syntax) + ); + } +} + +#[cfg(any(target_arch = "x86_64", rust_analyzer))] +mod imp { + use super::{Entry, Pointer}; + + #[allow(dead_code)] + #[repr(packed)] + struct Tss { + _0: u32, + rsp0: u64, + rsp1: u64, + rsp2: u64, + _1: u32, + ist1: u64, + ist2: u64, + ist3: u64, + ist4: u64, + ist5: u64, + ist6: u64, + ist7: u64, + _2: u64, + _3: u16, + iopb_base: u16, + } + + impl Tss { + const NULL: Self = Self { + _0: 0, + rsp0: 0, + rsp1: 0, + rsp2: 0, + _1: 0, + ist1: 0, + ist2: 0, + ist3: 0, + ist4: 0, + ist5: 0, + ist6: 0, + ist7: 0, + _2: 0, + _3: 0, + iopb_base: size_of::() as u16, + }; + } + + impl Entry { + const FLAG_LONG: u8 = 1 << 5; + + const RING0_CS64: Self = Entry::new( + 0, + 0, + Entry::FLAG_LONG, + Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE, + ); + const RING0_DS64: Self = Entry::new( + 0, + 0, + 0, + Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_WRITE, + ); + const RING3_DS64: Self = Entry::new( + 0, + 0, + 0, + Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_WRITE, + ); + const RING3_CS64: Self = Entry::new( + 0, + 0, + Entry::FLAG_LONG, + Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_EXECUTE, + ); + + const fn tss_low(base: u32, limit: u32) -> Self { + Self::new( + base, + limit, + Entry::FLAG_LONG, + Entry::ACC_ACCESS | Entry::ACC_PRESENT | Entry::ACC_EXECUTE, + ) + } + } + + pub fn create_gdt() -> (&'static [Entry], &'static Tss) { + // Won't be deallocated, so leaks are not a concern + let tss = Box::leak(Box::new(Tss::NULL)); + let tss_addr = (tss as *mut _).addr(); + let mut gdt = Box::new([ + Entry::NULL, + Entry::RING0_CS64, + Entry::RING0_DS64, + Entry::RING3_DS64, + Entry::RING3_CS64, + Entry::tss_low(tss_addr as u32, (size_of::() - 1) as u32), + Entry::NULL, + ]); + + let tss_high = &mut gdt[6] as *mut _ as *mut u64; + unsafe { tss_high.write_unaligned((tss_addr >> 32) as u64) }; + + Box::leak(gdt) + + // let gdt_addr = Box::into_raw(gdt) as usize; + + // todo!() + } + + pub unsafe fn load_gdt(gdt: &'static [Entry]) { + let gdtr = Pointer { + limit: (GDT_SIZE * size_of::()) as u16 - 1, + offset: gdt_addr, + }; + + core::arch::asm!( + r#" + wbinvd + lgdt ({0}) + + // Have to use iretq here + mov %rsp, %rcx + leaq 1f(%rip), %rax + + // SS:RSP + pushq $0x10 + pushq %rcx + + // RFLAGS + pushfq + + // CS:RIP + pushq $0x08 + pushq %rax + iretq + 1: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + mov $0x28, %ax + ltr %ax + "#, + in(reg) &gdtr, + out("rax") _, + out("rcx") _, + options(att_syntax) + ); + } +} + +pub use imp::*; + +/// Initializes the global descriptor table. +/// +/// # Safety +/// +/// Only meant to be called by the CPUs during their early init. +pub unsafe fn init() -> usize { + let (gdt, tss) = imp::create_gdt(); + imp::load_gdt(gdt); + (tss as *const Tss).addr() +} diff --git a/kernel/src/arch/x86_64/intrinsics.rs b/kernel/src/arch/x86/intrinsics.rs similarity index 95% rename from kernel/src/arch/x86_64/intrinsics.rs rename to kernel/src/arch/x86/intrinsics.rs index b1d9274c..93977f3a 100644 --- a/kernel/src/arch/x86_64/intrinsics.rs +++ b/kernel/src/arch/x86/intrinsics.rs @@ -1,4 +1,4 @@ -//! x86-64 architecture helper functions +//! i386 architecture helper functions use core::marker::PhantomData; @@ -136,3 +136,10 @@ pub fn flush_cpu_cache() { core::arch::asm!("wbinvd"); } } + +#[inline] +pub fn io_wait() { + WAIT_PORT.write(0); +} + +static WAIT_PORT: IoPort = IoPort::new(0x80); diff --git a/kernel/src/arch/x86/mod.rs b/kernel/src/arch/x86/mod.rs new file mode 100644 index 00000000..c765a342 --- /dev/null +++ b/kernel/src/arch/x86/mod.rs @@ -0,0 +1,13 @@ +//! x86-common stuff + +#![allow(missing_docs)] + +#[cfg(any(target_arch = "x86_64", rust_analyzer))] +pub const ISA_IRQ_OFFSET: u32 = ISA_IRQ_OFFSET; + +#[cfg(any(target_arch = "x86", rust_analyzer))] +pub const ISA_IRQ_OFFSET: u32 = 0; + +pub mod gdt; +pub mod intrinsics; +pub mod peripherals; diff --git a/kernel/src/arch/x86_64/peripherals/i8253.rs b/kernel/src/arch/x86/peripherals/i8253.rs similarity index 77% rename from kernel/src/arch/x86_64/peripherals/i8253.rs rename to kernel/src/arch/x86/peripherals/i8253.rs index a51351c1..0775501e 100644 --- a/kernel/src/arch/x86_64/peripherals/i8253.rs +++ b/kernel/src/arch/x86/peripherals/i8253.rs @@ -6,12 +6,13 @@ use device_api::{ timer::MonotonicTimestampProviderDevice, Device, }; -use libk::{device::external_interrupt_controller, task::runtime}; +use kernel_arch::task::Scheduler; +use libk::{arch::Cpu, device::external_interrupt_controller, task::runtime}; use libk_util::sync::IrqSafeSpinlock; -use crate::arch::x86_64::{ - apic::ioapic::ISA_IRQ_OFFSET, +use crate::arch::x86::{ intrinsics::{IoPort, IoPortAccess}, + ISA_IRQ_OFFSET, }; const FREQUENCY: u32 = 1193180; @@ -20,6 +21,8 @@ const CMD_CH0: u8 = 0 << 6; const CMD_ACC_WORD: u8 = 3 << 4; const CMD_MODE_RATE: u8 = 2 << 1; +pub static I8253: I8253 = I8253::new(); + struct Inner { ch0_data: IoPort, #[allow(unused)] @@ -81,7 +84,7 @@ impl Device for I8253 { } impl I8253 { - pub const fn new() -> Self { + const fn new() -> Self { Self { inner: IrqSafeSpinlock::new(Inner { ch0_data: IoPort::new(0x40), @@ -93,4 +96,20 @@ impl I8253 { }), } } + + pub fn irq_handler_fastpath(&self) { + let mut inner = self.inner.lock(); + inner.tick += 1; + + let now = Duration::from_millis(inner.tick); + drop(inner); + + runtime::tick(now); + + let cpu = Cpu::local(); + + if let Some(queue) = cpu.try_get_scheduler() { + unsafe { queue.yield_cpu() }; + } + } } diff --git a/kernel/src/arch/x86/peripherals/i8259.rs b/kernel/src/arch/x86/peripherals/i8259.rs new file mode 100644 index 00000000..b4896629 --- /dev/null +++ b/kernel/src/arch/x86/peripherals/i8259.rs @@ -0,0 +1,206 @@ +//! Intel 8259 interrupt controller + +use core::arch::global_asm; + +use abi::error::Error; +use device_api::{ + interrupt::{ + ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq, + IrqOptions, + }, + Device, +}; +use kernel_arch::task::Scheduler; +use kernel_arch_i686::context::InterruptFrame; +use libk::{ + arch::Cpu, + task::{sched, thread::Thread}, +}; +use libk_util::sync::IrqSafeSpinlock; + +use crate::arch::{ + i686::exception, + x86::intrinsics::{IoPort, IoPortAccess}, +}; + +use super::i8253::I8253; + +const ICW1_ICW4: u8 = 1 << 0; +const ICW1_INIT: u8 = 1 << 4; + +const ICW4_8086: u8 = 1 << 0; + +struct Inner { + master_cmd: IoPort, + master_data: IoPort, + slave_cmd: IoPort, + slave_data: IoPort, +} + +pub struct I8259 { + inner: IrqSafeSpinlock, + table: IrqSafeSpinlock>, +} + +impl Device for I8259 { + unsafe fn init(&'static self) -> Result<(), Error> { + self.enable(); + Ok(()) + } + + fn display_name(&self) -> &'static str { + "i8259 PIC" + } +} + +impl ExternalInterruptController for I8259 { + fn enable_irq(&self, irq: Irq) -> Result<(), Error> { + let Irq::External(irq) = irq else { panic!() }; + let inner = self.inner.lock(); + let port = if irq < 8 { + &inner.master_data + } else { + &inner.slave_data + }; + let mask = port.read(); + log::debug!("Enable IRQ#{}", irq); + port.write(mask & !(1 << (irq % 8))); + Ok(()) + } + + fn register_irq( + &self, + irq: Irq, + options: IrqOptions, + handler: &'static dyn InterruptHandler, + ) -> Result<(), Error> { + let Irq::External(irq) = irq else { + return Err(Error::InvalidArgument); + }; + if irq == 0 { + return Ok(()); + } + if irq >= 16 { + return Err(Error::InvalidArgument); + } + + let mut table = self.table.lock(); + + table.insert(irq as usize - 1, handler) + } + + fn handle_specific_irq(&self, index: usize) { + if index == 0 { + // Fastpath for timer + self.eoi(index); + I8253.irq_handler_fastpath(); + } else if index < 16 { + // Rest of IRQs + let table = self.table.lock(); + + if let Some(handler) = table.handler(index - 1) { + handler.handle_irq(None); + } else { + warnln!("No handler set for IRQ#{}", index); + } + + self.eoi(index); + } + } +} + +impl I8259 { + const fn new() -> Self { + let inner = Inner { + master_cmd: IoPort::new(0x20), + master_data: IoPort::new(0x21), + slave_cmd: IoPort::new(0xA0), + slave_data: IoPort::new(0xA1), + }; + + Self { + inner: IrqSafeSpinlock::new(inner), + table: IrqSafeSpinlock::new(FixedInterruptTable::new()), + } + } + + pub fn disable(&self) { + let inner = self.inner.lock(); + infoln!("Disabling i8259 PIC"); + + // Remap PIC IRQ vectors to 32.. + inner.master_cmd.write(ICW1_INIT | ICW1_ICW4); + inner.slave_cmd.write(ICW1_INIT | ICW1_ICW4); + + inner.master_data.write(32); + inner.slave_data.write(32 + 8); + + inner.slave_data.write(0xFF); + inner.master_data.write(0xFF); + + inner.master_cmd.write(0x20); + inner.slave_cmd.write(0x20); + } + + pub fn enable(&self) { + let inner = self.inner.lock(); + + // ICW1 + inner.master_cmd.write(ICW1_INIT | ICW1_ICW4); + inner.slave_cmd.write(ICW1_INIT | ICW1_ICW4); + + // ICW2 + inner.master_data.write(32); + inner.slave_data.write(32 + 8); + + // ICW3 + inner.master_data.write(4); + inner.slave_data.write(2); + + // ICW4 + inner.master_data.write(ICW4_8086); + inner.slave_data.write(ICW4_8086); + + // Mask everything + inner.master_data.write(0xFF); + inner.slave_data.write(0xFF); + } + + pub fn eoi(&self, index: usize) { + let inner = self.inner.lock(); + + inner.master_cmd.write(0x20); + if index >= 12 { + inner.slave_cmd.write(0x20); + } + } +} + +pub static I8259: I8259 = I8259::new(); + +pub fn setup_vectors(idt: &mut [exception::Entry]) { + extern "C" { + // IRQ vectors + static __i686_8259_vectors: [usize; 16]; + } + + for (i, &entry) in unsafe { __i686_8259_vectors.iter() }.enumerate() { + idt[i] = exception::Entry::new( + entry, + 0x08, + exception::Entry::PRESENT | exception::Entry::INT32, + ); + } +} + +extern "C" fn irq_handler(frame: *mut InterruptFrame) { + let frame = unsafe { &mut *frame }; + + I8259.handle_specific_irq(frame.irq_number as _); + + if let Some(thread) = Thread::get_current() { + unsafe { thread.handle_pending_signals(frame) }; + } +} + +global_asm!(include_str!("i8259_vectors.S"), irq_handler = sym irq_handler, options(att_syntax)); diff --git a/kernel/src/arch/x86/peripherals/i8259_vectors.S b/kernel/src/arch/x86/peripherals/i8259_vectors.S new file mode 100644 index 00000000..32633509 --- /dev/null +++ b/kernel/src/arch/x86/peripherals/i8259_vectors.S @@ -0,0 +1,85 @@ +.altmacro + +.global __i686_8259_vectors + +// TODO maybe use pushal/popal instead +.macro IRQ_SAVE_STATE + push %edi + push %esi + push %ebp + push %ebx + push %edx + push %ecx + push %eax +.endm + +.macro IRQ_RESTORE_STATE + pop %eax + pop %ecx + pop %edx + pop %ebx + pop %ebp + pop %esi + pop %edi +.endm + +.macro IRQ_VECTOR, n +irq_vector_\n: + pushl $\n + jmp irq_common_handler +.endm + +.macro IRQ_VECTOR_ENTRY, n +.long irq_vector_\n +.endm + +.macro IRQ_VECTORS, start, end +.set i, 0 +.rept \end - \start + IRQ_VECTOR %i + .set i, i+1 +.endr +.endm + +.macro IRQ_VECTOR_ENTRIES, start, end +.set i, 0 +.rept \end - \start + IRQ_VECTOR_ENTRY %i + .set i, i+1 +.endr +.endm + +.section .text +irq_common_handler: + // %esp + 0: interrupt number + // %esp + 4: eip + // %esp + 8: cs + // %esp + 12: eflags + // If change in CPL: + // %esp + 16: esp + // %esp + 20: ss + + IRQ_SAVE_STATE + + mov %esp, %ebp + + push %ebp + call {irq_handler} + + mov %ebp, %esp + + IRQ_RESTORE_STATE + + // Remove interrupt number + add $4, %esp + + iret + +IRQ_VECTORS 0, 16 + +.section .rodata +.p2align 4 +.type __i686_8259_vectors, @object +__i686_8259_vectors: + IRQ_VECTOR_ENTRIES 0, 16 +.size __i686_8259_vectors, . - __i686_8259_vectors diff --git a/kernel/src/arch/x86_64/peripherals/mod.rs b/kernel/src/arch/x86/peripherals/mod.rs similarity index 52% rename from kernel/src/arch/x86_64/peripherals/mod.rs rename to kernel/src/arch/x86/peripherals/mod.rs index 64a4ff6d..6b8577a9 100644 --- a/kernel/src/arch/x86_64/peripherals/mod.rs +++ b/kernel/src/arch/x86/peripherals/mod.rs @@ -1,5 +1,4 @@ -//! x86-64 platform peripheral drivers - pub mod i8253; +pub mod i8259; pub mod ps2; pub mod serial; diff --git a/kernel/src/arch/x86_64/peripherals/ps2/codeset.rs b/kernel/src/arch/x86/peripherals/ps2/codeset.rs similarity index 100% rename from kernel/src/arch/x86_64/peripherals/ps2/codeset.rs rename to kernel/src/arch/x86/peripherals/ps2/codeset.rs diff --git a/kernel/src/arch/x86_64/peripherals/ps2/mod.rs b/kernel/src/arch/x86/peripherals/ps2/mod.rs similarity index 91% rename from kernel/src/arch/x86_64/peripherals/ps2/mod.rs rename to kernel/src/arch/x86/peripherals/ps2/mod.rs index cf9315c3..972c06a8 100644 --- a/kernel/src/arch/x86_64/peripherals/ps2/mod.rs +++ b/kernel/src/arch/x86/peripherals/ps2/mod.rs @@ -10,9 +10,10 @@ use device_api::{ use libk::device::external_interrupt_controller; use libk_util::sync::IrqSafeSpinlock; -use crate::arch::x86_64::{ +use crate::arch::x86::{ intrinsics::{IoPort, IoPortAccess}, peripherals::ps2::codeset::{CODE_SET_1_00, CODE_SET_1_E0}, + ISA_IRQ_OFFSET, }; mod codeset; @@ -135,6 +136,7 @@ impl Device for PS2Controller { inner.send_command(0xAE); intc.enable_irq(self.primary_irq)?; + intc.enable_irq(self.auxiliary_irq)?; Ok(()) } @@ -142,7 +144,7 @@ impl Device for PS2Controller { impl PS2Controller { /// Constructs a new instance of the device - pub const fn new(primary_irq: Irq, auxiliary_irq: Irq, cmd_port: u16, data_port: u16) -> Self { + const fn new(primary_irq: Irq, auxiliary_irq: Irq, cmd_port: u16, data_port: u16) -> Self { let inner = Inner { command: IoPort::new(cmd_port), data: IoPort::new(data_port), @@ -155,3 +157,10 @@ impl PS2Controller { } } } + +pub static PS2: PS2Controller = PS2Controller::new( + Irq::External(ISA_IRQ_OFFSET + 1), + Irq::External(ISA_IRQ_OFFSET + 12), + 0x64, + 0x60, +); diff --git a/kernel/src/arch/x86_64/peripherals/serial.rs b/kernel/src/arch/x86/peripherals/serial.rs similarity index 96% rename from kernel/src/arch/x86_64/peripherals/serial.rs rename to kernel/src/arch/x86/peripherals/serial.rs index 9bd079ef..9e1748a8 100644 --- a/kernel/src/arch/x86_64/peripherals/serial.rs +++ b/kernel/src/arch/x86/peripherals/serial.rs @@ -4,7 +4,7 @@ use device_api::{interrupt::Irq, serial::SerialDevice, Device}; use libk_util::sync::IrqSafeSpinlock; use crate::{ - arch::x86_64::intrinsics::{IoPort, IoPortAccess}, + arch::x86::intrinsics::{IoPort, IoPortAccess}, debug::DebugSink, }; diff --git a/kernel/src/arch/x86_64/gdt.rs b/kernel/src/arch/x86_64/gdt.rs deleted file mode 100644 index 300f50e6..00000000 --- a/kernel/src/arch/x86_64/gdt.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! x86-64 Global Descriptor Table interface -use core::mem::size_of; - -use alloc::boxed::Box; - -#[allow(dead_code)] -#[repr(packed)] -struct Entry { - limit_lo: u16, - base_lo: u16, - base_mi: u8, - access: u8, - flags: u8, - base_hi: u8, -} - -#[allow(dead_code)] -#[repr(packed)] -struct Tss { - _0: u32, - rsp0: u64, - rsp1: u64, - rsp2: u64, - _1: u32, - ist1: u64, - ist2: u64, - ist3: u64, - ist4: u64, - ist5: u64, - ist6: u64, - ist7: u64, - _2: u64, - _3: u16, - iopb_base: u16, -} - -#[allow(dead_code)] -#[repr(packed)] -struct Pointer { - limit: u16, - offset: usize, -} - -impl Tss { - const NULL: Self = Self { - _0: 0, - rsp0: 0, - rsp1: 0, - rsp2: 0, - _1: 0, - ist1: 0, - ist2: 0, - ist3: 0, - ist4: 0, - ist5: 0, - ist6: 0, - ist7: 0, - _2: 0, - _3: 0, - iopb_base: size_of::() as u16, - }; -} - -impl Entry { - const FLAG_LONG: u8 = 1 << 5; - const ACC_PRESENT: u8 = 1 << 7; - const ACC_SYSTEM: u8 = 1 << 4; - const ACC_EXECUTE: u8 = 1 << 3; - const ACC_WRITE: u8 = 1 << 1; - - #[allow(unused)] - const ACC_RING3: u8 = 3 << 5; - #[allow(unused)] - const ACC_ACCESS: u8 = 1 << 0; - - const NULL: Self = Self { - base_lo: 0, - base_mi: 0, - base_hi: 0, - access: 0, - flags: 0, - limit_lo: 0, - }; - const RING0_CS64: Self = Entry::new( - 0, - 0, - Entry::FLAG_LONG, - Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE, - ); - const RING0_DS64: Self = Entry::new( - 0, - 0, - 0, - Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_WRITE, - ); - const RING3_DS64: Self = Entry::new( - 0, - 0, - 0, - Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_WRITE, - ); - const RING3_CS64: Self = Entry::new( - 0, - 0, - Entry::FLAG_LONG, - Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_EXECUTE, - ); - - const fn new(base: u32, limit: u32, flags: u8, access: u8) -> Self { - Self { - base_lo: (base & 0xFFFF) as u16, - base_mi: ((base >> 16) & 0xFF) as u8, - base_hi: ((base >> 24) & 0xFF) as u8, - access, - flags: (flags & 0xF0) | (((limit >> 16) & 0xF) as u8), - limit_lo: (limit & 0xFFFF) as u16, - } - } - - const fn tss_low(base: u32, limit: u32) -> Self { - Self::new( - base, - limit, - Entry::FLAG_LONG, - Entry::ACC_ACCESS | Entry::ACC_PRESENT | Entry::ACC_EXECUTE, - ) - } -} - -// NULL, CS64, DS64, DS64, CS64, TSS, TSSext -const GDT_SIZE: usize = 7; - -/// Initializes the global descriptor table. -/// -/// # Safety -/// -/// Only meant to be called by the CPUs during their early init. -pub unsafe fn init() -> usize { - // Won't be deallocated, so leaks are not a concern - let tss_addr = Box::into_raw(Box::new(Tss::NULL)) as usize; - let mut gdt = Box::new([ - Entry::NULL, - Entry::RING0_CS64, - Entry::RING0_DS64, - Entry::RING3_DS64, - Entry::RING3_CS64, - Entry::tss_low(tss_addr as u32, (size_of::() - 1) as u32), - Entry::NULL, - ]); - - let tss_high = &mut gdt[6] as *mut _ as *mut u64; - tss_high.write_unaligned((tss_addr >> 32) as u64); - - let gdt_addr = Box::into_raw(gdt) as usize; - - let gdtr = Pointer { - limit: (GDT_SIZE * size_of::()) as u16 - 1, - offset: gdt_addr, - }; - - core::arch::asm!( - r#" - wbinvd - lgdt ({0}) - - // Have to use iretq here - mov %rsp, %rcx - leaq 1f(%rip), %rax - - // SS:RSP - pushq $0x10 - pushq %rcx - - // RFLAGS - pushfq - - // CS:RIP - pushq $0x08 - pushq %rax - iretq - 1: - mov $0x10, %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - mov %ax, %ss - - mov $0x28, %ax - ltr %ax - "#, - in(reg) &gdtr, - out("rax") _, - out("rcx") _, - options(att_syntax) - ); - - tss_addr -} diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index a0898c24..a87e5568 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -31,16 +31,18 @@ mod apic; mod boot; mod cpuid; mod exception; -mod gdt; +#[path = "../i686/intrinsics.rs"] mod intrinsics; -mod peripherals; mod smp; mod syscall; use crate::{ - arch::x86_64::{ - apic::ioapic::ISA_IRQ_OFFSET, - intrinsics::{IoPort, IoPortAccess}, + arch::{ + x86::peripherals::{i8253::I8253, i8259::I8259, ps2::PS2}, + x86_64::{ + apic::ioapic::ISA_IRQ_OFFSET, + intrinsics::{IoPort, IoPortAccess}, + }, }, debug::{self, LogLevel}, device::{ @@ -311,10 +313,9 @@ impl X86_64 { self.init_acpi_from_boot_data()?; - Self::disable_8259(); + I8259.disable(); - let timer = Box::leak(Box::new(I8253::new())); - register_monotonic_timestamp_provider(timer); + register_monotonic_timestamp_provider(&I8253); let com1_3 = Box::leak(Box::new(ComPort::new( 0x3F8, @@ -333,23 +334,17 @@ impl X86_64 { git_version!() ); - let ps2 = Box::leak(Box::new(PS2Controller::new( - Irq::External(ISA_IRQ_OFFSET + 1), - Irq::External(ISA_IRQ_OFFSET + 12), - 0x64, - 0x60, - ))); - ps2.init()?; + PS2.init()?; if let Some(acpi) = self.acpi.try_get() { self.init_platform_from_acpi(acpi)?; } - timer.init_irq()?; + I8253.init_irq()?; // ps2.connect(self.tty.get()); ps2.init_irq()?; - device::register_device(ps2); + device::register_device(&PS2); PciBusManager::setup_bus_devices()?; } @@ -454,26 +449,4 @@ impl X86_64 { INITRD_DATA.init(initrd); } - - unsafe fn disable_8259() { - infoln!("Disabling i8259 PIC"); - // TODO should I make a module for 8259 if I don't even use it? - let pic_master_cmd = IoPort::::new(0x20); - let pic_master_data = IoPort::::new(0x21); - let pic_slave_cmd = IoPort::::new(0xA0); - let pic_slave_data = IoPort::::new(0xA1); - - // Remap PIC IRQ vectors to 32.. - pic_master_cmd.write(0x11); - pic_slave_cmd.write(0x11); - - pic_master_data.write(32); - pic_slave_data.write(32 + 8); - - pic_slave_data.write(0xFF); - pic_master_data.write(0xFF); - - pic_master_cmd.write(0x20); - pic_slave_cmd.write(0x20); - } } diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs index 1ed028d9..3f088a47 100644 --- a/kernel/src/debug.rs +++ b/kernel/src/debug.rs @@ -263,7 +263,7 @@ pub fn init() { pub fn debug_internal(args: Arguments, level: LogLevel) { use fmt::Write; - RING_LOGGER_SINK.write_fmt(args).ok(); + // RING_LOGGER_SINK.write_fmt(args).ok(); for sink in DEBUG_SINKS.lock().iter_mut() { if level < sink.level { diff --git a/kernel/src/device/display/console.rs b/kernel/src/device/display/console.rs index 3befb6ce..964ca4b8 100644 --- a/kernel/src/device/display/console.rs +++ b/kernel/src/device/display/console.rs @@ -2,10 +2,10 @@ use core::time::Duration; -use abi::{error::Error, primitive_enum}; +use abi::{error::Error, io::TerminalSize, primitive_enum}; use alloc::{vec, vec::Vec}; use bitflags::bitflags; -use libk::task::runtime; +use libk::{task::runtime, vfs::TerminalOutput}; use libk_util::{sync::IrqSafeSpinlock, StaticVector}; use crate::debug::DebugSink; @@ -91,6 +91,9 @@ pub struct ConsoleBuffer { height: u32, } +/// Console wrapper for adding it into devfs as a char device +pub struct ConsoleWrapper<'a>(pub &'a dyn DisplayConsole); + enum EscapeState { Normal, Escape, @@ -124,7 +127,7 @@ pub struct RowIter<'a> { } /// Interface to implement buffered console semantics on an abstract console output device -pub trait DisplayConsole { +pub trait DisplayConsole: Sync { /// Returns the state lock fn state(&self) -> &IrqSafeSpinlock; @@ -533,6 +536,18 @@ impl DebugSink for dyn DisplayConsole { } } +impl TerminalOutput for ConsoleWrapper<'_> { + fn size(&self) -> TerminalSize { + let (rows, columns) = self.0.text_dimensions(); + TerminalSize { rows, columns } + } + + fn write(&self, byte: u8) -> Result<(), Error> { + self.0.write_char(byte); + Ok(()) + } +} + static CONSOLES: IrqSafeSpinlock> = IrqSafeSpinlock::new(Vec::new()); diff --git a/kernel/src/init.rs b/kernel/src/init.rs index d3dc1939..bd383235 100644 --- a/kernel/src/init.rs +++ b/kernel/src/init.rs @@ -54,6 +54,7 @@ pub fn kinit() -> Result<(), Error> { { use crate::device::display::console::update_consoles_task; + log::info!("Spawn update consoles task"); runtime::spawn(async move { update_consoles_task().await; })?; @@ -70,7 +71,7 @@ pub fn kinit() -> Result<(), Error> { // TODO move this to userspace so it doesn't block the init process, maybe lazy-load on first // attempt to load a module? - load_kernel_symbol_table(&mut ioctx, "/kernel.sym")?; + // load_kernel_symbol_table(&mut ioctx, "/kernel.sym")?; { let group_id = Process::create_group(); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 6d8e4e52..2fcb5b7a 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -37,6 +37,7 @@ #![no_std] #![no_main] +use alloc::boxed::Box; use arch::Platform; use kernel_arch::{Architecture, ArchitectureImpl}; use libk::arch::Cpu; diff --git a/kernel/src/syscall/imp/sys_io.rs b/kernel/src/syscall/imp/sys_io.rs index 86abcabb..2ef45543 100644 --- a/kernel/src/syscall/imp/sys_io.rs +++ b/kernel/src/syscall/imp/sys_io.rs @@ -27,7 +27,7 @@ pub(crate) fn open( let thread = Thread::current(); let process = thread.process(); - run_with_io_at(&process, at, |at, mut io| { + let res = run_with_io_at(&process, at, |at, mut io| { let file = io.ioctx_mut().open(Some(at), path, opts, mode)?; // TODO NO_CTTY? @@ -41,7 +41,9 @@ pub(crate) fn open( let fd = io.files.place_file(file, true)?; Ok(fd) - }) + }); + log::debug!("Open {:?} -> {:?}", path, res); + res } pub(crate) fn close(fd: RawFd) -> Result<(), Error> { diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 203f71ee..4b114751 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -75,8 +75,8 @@ mod generated { } /// Entrypoint for system calls that takes raw argument values -pub fn raw_syscall_handler(func: u64, args: &[u64]) -> u64 { - let Ok(func) = SyscallFunction::try_from(func as usize) else { +pub fn raw_syscall_handler(func: usize, args: &[usize]) -> u64 { + let Ok(func) = SyscallFunction::try_from(func) else { todo!("Undefined syscall: {}", func); }; let args = unsafe { core::mem::transmute(args) }; diff --git a/kernel/src/task/mod.rs b/kernel/src/task/mod.rs index 5a44a891..b60a074a 100644 --- a/kernel/src/task/mod.rs +++ b/kernel/src/task/mod.rs @@ -45,6 +45,7 @@ pub fn init() -> Result<(), Error> { // Spawn async workers (0..cpu_count).for_each(|index| { + log::debug!("Spawn async worker #{}", index); runtime::spawn_async_worker(index).unwrap(); }); @@ -64,8 +65,9 @@ pub unsafe fn enter() -> ! { static AP_CAN_ENTER: SpinFence = SpinFence::new(); let mut cpu = Cpu::local(); + let id = cpu.id(); - if cpu.id() != 0 { + if id != 0 { // Wait until BSP allows us to enter AP_CAN_ENTER.wait_one(); } else { diff --git a/kernel/src/util/mod.rs b/kernel/src/util/mod.rs index 3a9a0539..87e0c5b9 100644 --- a/kernel/src/util/mod.rs +++ b/kernel/src/util/mod.rs @@ -50,4 +50,8 @@ pub const fn arch_str() -> &'static str { { "x86_64" } + #[cfg(target_arch = "x86")] + { + "i686" + } } diff --git a/kernel/tools/gentables/src/main.rs b/kernel/tools/gentables/src/main.rs index 561ef644..7a9f0e27 100644 --- a/kernel/tools/gentables/src/main.rs +++ b/kernel/tools/gentables/src/main.rs @@ -42,6 +42,10 @@ pub enum GenError { MissingSymbolTable, #[error("Incorrect tables section placement: {0:#x}")] IncorrectTablesPlacement(u64), + #[error("Incorrect tables section size: got {0}, expected {1}")] + IncorrectTablesSize(u64, usize), + #[error("Incorrect tables section alignment: expected at least 4K, got {0}")] + IncorrectTablesAlign(u64), } #[derive(Parser)] @@ -108,6 +112,11 @@ fn kernel_virt_offset(elf: &mut ElfStream) -> Resu } fn find_tables(elf: &mut ElfStream) -> Result<(u64, u64), GenError> { + let section_size = match elf.ehdr.e_machine { + EM_AARCH64 => size_of::(), + EM_X86_64 => size_of::(), + _ => unimplemented!(), + }; let (shdrs, strtab) = elf.section_headers_with_strtab()?; let strtab = strtab.ok_or_else(|| GenError::MissingSection(".strtab"))?; @@ -115,6 +124,13 @@ fn find_tables(elf: &mut ElfStream) -> Result<(u64 let name = strtab.get(shdr.sh_name as _)?; if name == ".data.tables" { + if shdr.sh_size != section_size as _ { + return Err(GenError::IncorrectTablesSize(shdr.sh_size, section_size)); + } + if shdr.sh_addralign < 0x1000 { + return Err(GenError::IncorrectTablesAlign(shdr.sh_addralign)); + } + // TODO section checks return Ok((shdr.sh_offset, shdr.sh_addr)); } diff --git a/lib/abi/build.rs b/lib/abi/build.rs index 20583283..b6cd444c 100644 --- a/lib/abi/build.rs +++ b/lib/abi/build.rs @@ -1,4 +1,4 @@ -use std::{fs, path::Path}; +use std::{env, fs, path::Path}; use abi_generator::{ abi::{ty::TypeWidth, AbiBuilder}, @@ -18,19 +18,24 @@ fn load_abi_definitions>(path: P) -> String { content } -fn generate_abi>(abi_path: P) { +fn generate_abi>(target_is_64bit: bool, abi_path: P) { let output_dir = std::env::var("OUT_DIR").expect("$OUT_DIR not set"); let generated_types = Path::new(&output_dir).join("generated_types.rs"); - let abi = load_abi_definitions(abi_path); - let abi = AbiBuilder::from_string( - &abi, + let target_env = if target_is_64bit { TargetEnv { thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + } + } else { + TargetEnv { + thin_pointer_width: TypeWidth::U32, fat_pointer_width: TypeWidth::U64, - }, - ) - .unwrap_fancy(&abi); + } + }; + + let abi = load_abi_definitions(abi_path); + let abi = AbiBuilder::from_string(&abi, target_env).unwrap_fancy(&abi); let types = prettyplease::unparse( &abi.emit_file(true, false) @@ -41,5 +46,6 @@ fn generate_abi>(abi_path: P) { } fn main() { - generate_abi("def"); + let target_is_64bit = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "64"; + generate_abi(target_is_64bit, "def"); } diff --git a/lib/abi/def/io.abi b/lib/abi/def/io.abi index 96376146..ccabf2e0 100644 --- a/lib/abi/def/io.abi +++ b/lib/abi/def/io.abi @@ -2,7 +2,7 @@ // abi::io -newtype RawFd(u32); +newtype RawFd(u31); newtype UserId(u32); newtype GroupId(u32); diff --git a/lib/abi/src/arch/i686.rs b/lib/abi/src/arch/i686.rs new file mode 100644 index 00000000..4a5b8158 --- /dev/null +++ b/lib/abi/src/arch/i686.rs @@ -0,0 +1,18 @@ +#![allow(missing_docs)] + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Default)] +#[repr(C)] +pub struct SavedFrame { + pub eax: u32, + pub ecx: u32, + pub edx: u32, + pub ebx: u32, + pub ebp: u32, + pub esi: u32, + pub edi: u32, + + pub user_ip: u32, + pub user_sp: u32, + pub eflags: u32, +} diff --git a/lib/abi/src/arch/mod.rs b/lib/abi/src/arch/mod.rs index 57a6ad25..88c30fb6 100644 --- a/lib/abi/src/arch/mod.rs +++ b/lib/abi/src/arch/mod.rs @@ -10,4 +10,9 @@ pub(crate) mod x86_64; #[cfg(target_arch = "x86_64")] use x86_64 as arch_impl; +#[cfg(target_arch = "x86")] +pub(crate) mod i686; +#[cfg(target_arch = "x86")] +use i686 as arch_impl; + pub use arch_impl::SavedFrame; diff --git a/lib/qemu/src/i386.rs b/lib/qemu/src/i386.rs new file mode 100644 index 00000000..a276cda9 --- /dev/null +++ b/lib/qemu/src/i386.rs @@ -0,0 +1,88 @@ +use std::{path::PathBuf, process::Command}; + +use crate::{Architecture, IntoArgs}; + +#[derive(Debug)] +pub enum Machine { + Q35, + Pc, +} + +#[derive(Debug)] +pub enum Cpu { + Host { enable_kvm: bool }, +} + +#[derive(Debug)] +pub enum Image { + Drive(PathBuf), +} + +#[derive(Debug, Default)] +pub struct QemuI386 { + pub bios_image: Option, + pub boot_slot: Option, +} + +impl IntoArgs for Machine { + fn add_args(&self, command: &mut Command) { + match self { + Self::Q35 => { + command.args(["-M", "q35"]); + } + Self::Pc => { + command.args(["-M", "pc"]); + } + } + } +} + +impl IntoArgs for Cpu { + fn add_args(&self, command: &mut Command) { + match self { + &Self::Host { enable_kvm } => { + command.args(["-cpu", "host"]); + if enable_kvm { + command.arg("-enable-kvm"); + } + } + } + } +} + +impl IntoArgs for Image { + fn add_args(&self, command: &mut Command) { + match self { + Self::Drive(path) => { + command.args(["-drive", &format!("format=raw,file={}", path.display())]); + } + } + } +} + +impl IntoArgs for QemuI386 { + fn add_args(&self, command: &mut Command) { + if let Some(slot) = self.boot_slot { + command.arg("-boot"); + command.arg(format!("{}", slot)); + } + + if let Some(bios_image) = self.bios_image.as_ref() { + command.args([ + "-drive", + &format!( + "format=raw,if=pflash,readonly=on,file={}", + bios_image.display() + ), + ]); + } + } +} + +impl Architecture for QemuI386 { + type MachineType = Machine; + type CpuType = Cpu; + type ImageType = Image; + + const DEFAULT_COMMAND: &'static str = "qemu-system-i386"; +} diff --git a/lib/qemu/src/lib.rs b/lib/qemu/src/lib.rs index 72c2e69a..9ac9967f 100644 --- a/lib/qemu/src/lib.rs +++ b/lib/qemu/src/lib.rs @@ -1,8 +1,13 @@ -use std::{fmt::Debug, path::PathBuf, process::Command}; +use std::{ + fmt::Debug, + path::{Path, PathBuf}, + process::Command, +}; use device::{QemuDevice, QemuSerialTarget}; pub mod aarch64; +pub mod i386; pub mod x86_64; pub mod device; @@ -40,6 +45,7 @@ pub struct Qemu { cpu: Option, machine: Option, boot_image: Option, + cdrom: Option, no_display: bool, memory_megabytes: Option, smp: Option, @@ -68,6 +74,12 @@ impl Qemu { } } +impl Qemu { + pub fn new_i386() -> Self { + Qemu::new(i386::QemuI386::default()) + } +} + impl Qemu { pub fn new(arch: A) -> Self { Self { @@ -79,6 +91,7 @@ impl Qemu { cpu: None, machine: None, boot_image: None, + cdrom: None, smp: None, arch, } @@ -104,6 +117,11 @@ impl Qemu { self } + pub fn with_cdrom>(&mut self, path: P) -> &mut Self { + self.cdrom = Some(path.as_ref().into()); + self + } + pub fn with_cpu(&mut self, cpu: A::CpuType) -> &mut Self { self.cpu = Some(cpu); self @@ -166,6 +184,10 @@ impl IntoArgs for Qemu { self.cpu.add_args(command); self.boot_image.add_args(command); + if let Some(cdrom) = &self.cdrom { + command.args(["-cdrom", &format!("{}", cdrom.display())]); + } + if self.no_display { command.args(["-display", "none"]); } diff --git a/lib/runtime/build.rs b/lib/runtime/build.rs index 127d159c..7a4aef31 100644 --- a/lib/runtime/build.rs +++ b/lib/runtime/build.rs @@ -1,4 +1,4 @@ -use std::{fs, path::Path}; +use std::{env, fs, path::Path}; use abi_generator::{ abi::{ty::TypeWidth, AbiBuilder}, @@ -50,19 +50,24 @@ fn build_libm() { build.compile("m"); } -fn generate_abi>(abi_path: P) { +fn generate_abi>(target_is_64bit: bool, abi_path: P) { let output_dir = std::env::var("OUT_DIR").expect("$OUT_DIR not set"); let generated_calls = Path::new(&output_dir).join("generated_calls.rs"); - let abi = load_abi_definitions(abi_path); - let abi = AbiBuilder::from_string( - &abi, + let target_env = if target_is_64bit { TargetEnv { thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + } + } else { + TargetEnv { + thin_pointer_width: TypeWidth::U32, fat_pointer_width: TypeWidth::U64, - }, - ) - .unwrap_fancy(&abi); + } + }; + + let abi = load_abi_definitions(abi_path); + let abi = AbiBuilder::from_string(&abi, target_env).unwrap_fancy(&abi); let calls = prettyplease::unparse( &abi.emit_file(false, true) @@ -73,7 +78,8 @@ fn generate_abi>(abi_path: P) { } fn main() { + let target_is_64bit = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "64"; println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); build_libm(); - generate_abi("../abi/def"); + generate_abi(target_is_64bit, "../abi/def"); } diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 6f34330b..5247f48c 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -3,6 +3,7 @@ #![no_std] #![deny(missing_docs)] #![allow(nonstandard_style)] +#![allow(unused)] #[cfg(not(feature = "rustc-dep-of-std"))] extern crate compiler_builtins; diff --git a/lib/runtime/src/sys/i686.rs b/lib/runtime/src/sys/i686.rs new file mode 100644 index 00000000..757a9701 --- /dev/null +++ b/lib/runtime/src/sys/i686.rs @@ -0,0 +1,95 @@ +/// i686 implementation of a system call macro + +#[macro_export] +macro_rules! syscall { + ($num:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!("int $0x80", inlateout("eax") res); + res + }}; + + ($num:expr, $a0:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!( + "int $0x80", + inlateout("eax") res, + in("ebx") $a0, + ); + res + }}; + + ($num:expr, $a0:expr, $a1:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!( + "int $0x80", + inlateout("eax") res, + in("ebx") $a0, + in("ecx") $a1 + ); + res + }}; + + ($num:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!( + "int $0x80", + inlateout("eax") res, + in("ebx") $a0, + in("ecx") $a1, + in("edx") $a2 + ); + res + }}; + + ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!( + r#" + xchg %esi, {a3} + int $0x80 + xchg %esi, {a3} + "#, + a3 = in(reg) $a3, + inlateout("eax") res, + in("ebx") $a0, + in("ecx") $a1, + in("edx") $a2, + options(nostack, att_syntax) + ); + res + }}; + + ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!( + r#" + xchg %esi, {a3} + int $0x80 + xchg %esi, {a3} + "#, + a3 = in(reg) $a3, + inlateout("eax") res, + in("ebx") $a0, + in("ecx") $a1, + in("edx") $a2, + in("edi") $a4, + options(nostack, att_syntax) + ); + res + }}; + + ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr $(,)?) => {{ + let mut res = usize::from($num); + core::arch::asm!( + r#" + int $0x81 + "#, + inlateout("eax") res, + in("ebx") $a0, + in("ecx") $a1, + in("edx") $a2, + options(nostack, att_syntax) + ); + res + }}; +} diff --git a/lib/runtime/src/sys/mod.rs b/lib/runtime/src/sys/mod.rs index b9a9f0cf..1e06ac46 100644 --- a/lib/runtime/src/sys/mod.rs +++ b/lib/runtime/src/sys/mod.rs @@ -6,6 +6,9 @@ mod aarch64; #[cfg(target_arch = "x86_64")] #[macro_use] mod x86_64; +#[cfg(target_arch = "x86")] +#[macro_use] +mod i686; #[allow(missing_docs)] mod generated { diff --git a/tool/abi-generator/src/abi/mod.rs b/tool/abi-generator/src/abi/mod.rs index af98a7bc..84f06cf9 100644 --- a/tool/abi-generator/src/abi/mod.rs +++ b/tool/abi-generator/src/abi/mod.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, path::Path, rc::Rc}; use proc_macro2::{Span, TokenStream}; use quote::{quote, TokenStreamExt}; -use syn::{punctuated::Punctuated, spanned::Spanned, token, Token}; +use syn::{punctuated::Punctuated, spanned::Spanned, token, Ident, Token}; pub mod ty; @@ -371,32 +371,37 @@ fn generate_transparent_struct( is_thin: bool, ) -> TokenStream { let TypeRepr(repr) = &repr; + let real_repr = match repr.to_string().as_str() { + "u31" => Ident::new("u32", repr.span()), + "u63" => Ident::new("u64", repr.span()), + _ => repr.clone(), + }; let attributes = attributes.filtered(|attr| !attr.path().is_ident("default")); let direct = quote! { #attributes #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] - pub struct #name(pub(crate) #repr); + pub struct #name(pub(crate) #real_repr); impl #name { - pub const fn into_raw(self) -> #repr { + pub const fn into_raw(self) -> #real_repr { self.0 } - pub const fn bits(self) -> #repr { + pub const fn bits(self) -> #real_repr { self.0 } #[allow(clippy::missing_safety_doc)] - pub const unsafe fn from_raw(value: #repr) -> Self { + pub const unsafe fn from_raw(value: #real_repr) -> Self { Self(value) } } - impl From<#name> for #repr { + impl From<#name> for #real_repr { #[inline] - fn from(value: #name) -> #repr { + fn from(value: #name) -> #real_repr { value.0 } } diff --git a/tool/abi-generator/src/abi/ty/mod.rs b/tool/abi-generator/src/abi/ty/mod.rs index 5225aa6b..d07646a9 100644 --- a/tool/abi-generator/src/abi/ty/mod.rs +++ b/tool/abi-generator/src/abi/ty/mod.rs @@ -62,6 +62,7 @@ pub enum TypeWidth { Zero, U8, U16, + U31, U32, U64, U128, @@ -74,6 +75,7 @@ impl TypeWidth { Self::Zero => todo!(), Self::U8 => Self::U16, Self::U16 => Self::U32, + Self::U31 => Self::U64, Self::U32 => Self::U64, Self::U64 => Self::U128, Self::U128 => todo!(), diff --git a/tool/abi-generator/src/abi/ty/primitive.rs b/tool/abi-generator/src/abi/ty/primitive.rs index d4082465..eb92be9a 100644 --- a/tool/abi-generator/src/abi/ty/primitive.rs +++ b/tool/abi-generator/src/abi/ty/primitive.rs @@ -16,6 +16,7 @@ pub enum AbiPrimitive { I8, U16, I16, + U31, U32, I32, U64, @@ -30,6 +31,7 @@ impl Type for AbiPrimitive { Self::Unit => TypeWidth::Zero, Self::U8 | Self::I8 => TypeWidth::U8, Self::U16 | Self::I16 => TypeWidth::U16, + Self::U31 => TypeWidth::U31, Self::U32 | Self::I32 => TypeWidth::U32, Self::U64 | Self::I64 => TypeWidth::U64, Self::U128 => TypeWidth::U128, @@ -57,26 +59,30 @@ impl Type for AbiPrimitive { Self::U128 => quote!(u128), Self::USize => quote!(usize), Self::Bool => quote!(bool), + + Self::U31 => todo!(), } } - fn emit_to_syscall_arguments(&self, _env: &TargetEnv, value: &Ident) -> TokenStream { + fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream { match self { Self::USize => quote!(#value), Self::U128 => todo!("Emit to syscall for u128"), + Self::U64 if env.thin_pointer_width == TypeWidth::U32 => todo!(), _ => quote!(#value as usize), } } fn emit_from_syscall_arguments( &self, - _env: &TargetEnv, + env: &TargetEnv, args: &Ident, index: usize, ) -> (TokenStream, usize) { match self { Self::Bool => (quote!(#args[#index] != 0), 1), Self::U128 => todo!("Emit from syscall for u128"), + Self::U64 if env.thin_pointer_width == TypeWidth::U32 => todo!(), _ => { let ty = self.as_rust_type(); (quote!(#args[#index] as #ty), 1) @@ -95,6 +101,7 @@ impl FromStr for AbiPrimitive { "i16" => Ok(Self::I16), "u16" => Ok(Self::U16), "i32" => Ok(Self::I32), + "u31" => Ok(Self::U31), "u32" => Ok(Self::U32), "i64" => Ok(Self::I64), "u64" => Ok(Self::U64), diff --git a/userspace/Cargo.lock b/userspace/Cargo.lock index ce07a383..a59ae272 100644 --- a/userspace/Cargo.lock +++ b/userspace/Cargo.lock @@ -16,54 +16,12 @@ dependencies = [ name = "abi-lib" version = "0.1.0" -[[package]] -name = "anstream" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "arrayvec" version = "0.7.4" @@ -88,15 +46,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "bytemuck" version = "1.15.0" @@ -160,10 +109,8 @@ version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ - "anstream", "anstyle", "clap_lex", - "strsim", ] [[package]] @@ -184,12 +131,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "colors" version = "0.1.0" @@ -203,15 +144,6 @@ dependencies = [ "yggdrasil-abi", ] -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - [[package]] name = "crossterm" version = "0.27.0" @@ -237,16 +169,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "deranged" version = "0.3.11" @@ -256,31 +178,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dyn-loader" -version = "0.1.0" -dependencies = [ - "elf", - "thiserror", - "yggdrasil-rt", -] - -[[package]] -name = "elf" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" - [[package]] name = "equivalent" version = "1.0.1" @@ -343,16 +240,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.12" @@ -395,15 +282,6 @@ dependencies = [ "libm", ] -[[package]] -name = "iced-x86" -version = "1.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" -dependencies = [ - "lazy_static", -] - [[package]] name = "idna" version = "0.5.0" @@ -724,12 +602,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "rangemap" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" - [[package]] name = "raqote" version = "0.8.3" @@ -742,22 +614,6 @@ dependencies = [ "typed-arena", ] -[[package]] -name = "rdb" -version = "0.1.0" -dependencies = [ - "clap", - "elf", - "iced-x86", - "libterm", - "rangemap", - "serde", - "serde_json", - "thiserror", - "yggdrasil-abi", - "yggdrasil-rt", -] - [[package]] name = "red" version = "0.1.0" @@ -845,17 +701,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shell" version = "0.1.0" @@ -904,12 +749,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" -[[package]] -name = "strsim" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - [[package]] name = "sw-composite" version = "0.7.16" @@ -962,7 +801,6 @@ dependencies = [ "rand", "serde", "serde_json", - "sha2", "thiserror", "yggdrasil-abi", "yggdrasil-rt", @@ -1081,12 +919,6 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -1125,12 +957,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "version_check" version = "0.9.4" diff --git a/userspace/Cargo.toml b/userspace/Cargo.toml index 20c949ad..d3dbf8f2 100644 --- a/userspace/Cargo.toml +++ b/userspace/Cargo.toml @@ -12,7 +12,7 @@ members = [ "lib/libterm", "netutils", "netutils", - "dyn-loader", - "rdb" +# "dyn-loader", +# "rdb" ] exclude = ["dynload-program", "test-kernel-module"] diff --git a/userspace/arch/i686/inittab b/userspace/arch/i686/inittab new file mode 100644 index 00000000..f324faec --- /dev/null +++ b/userspace/arch/i686/inittab @@ -0,0 +1,4 @@ +init:1:wait:/sbin/rc default +logd:1:once:/sbin/logd + +user:1:once:/sbin/login /dev/tty0 diff --git a/userspace/init/src/main.rs b/userspace/init/src/main.rs index 3842777b..44965a33 100644 --- a/userspace/init/src/main.rs +++ b/userspace/init/src/main.rs @@ -184,6 +184,7 @@ fn main() -> ExitCode { debug_trace!("Rules loaded"); for rule in rules { + debug_trace!("rc: {:?}", rule); if let Err(err) = rule.run() { debug_trace!("rc: failed to execute rule {:?}: {:?}", rule, err); } diff --git a/userspace/sysutils/Cargo.toml b/userspace/sysutils/Cargo.toml index 6edcb553..be0bdd1f 100644 --- a/userspace/sysutils/Cargo.toml +++ b/userspace/sysutils/Cargo.toml @@ -18,7 +18,7 @@ humansize = { version = "2.1.3", features = ["impl_style"] } rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil" } serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.111" -sha2 = { version = "0.10.8" } +# sha2 = { version = "0.10.8" } init = { path = "../init" } @@ -87,9 +87,9 @@ path = "src/view.rs" name = "chmod" path = "src/chmod.rs" -[[bin]] -name = "sha256sum" -path = "src/sha256sum.rs" +# [[bin]] +# name = "sha256sum" +# path = "src/sha256sum.rs" [[bin]] name = "sysmon" diff --git a/xtask/src/build/i686.rs b/xtask/src/build/i686.rs new file mode 100644 index 00000000..774f7609 --- /dev/null +++ b/xtask/src/build/i686.rs @@ -0,0 +1,36 @@ +use std::fs::{self, File}; + +use crate::{env::BuildEnv, error::Error, util::run_external_command}; + +use super::{ImageBuilt, InitrdGenerated, KernelProcessed}; + +pub fn build_image( + env: &BuildEnv, + kernel: KernelProcessed, + initrd: InitrdGenerated, +) -> Result { + let image_dir = env.kernel_output_dir.join("image"); + let grub_dir = image_dir.join("boot/grub"); + let image = env.kernel_output_dir.join("image.iso"); + + fs::create_dir_all(&grub_dir)?; + fs::copy(&kernel.0 .0, image_dir.join("kernel.elf"))?; + fs::copy(&initrd.0, image_dir.join("initrd.img"))?; + fs::write( + grub_dir.join("grub.cfg"), + br#" +menuentry "Yggdrasil" { + multiboot /kernel.elf + module /initrd.img +} +"#, + )?; + + run_external_command( + "grub-mkrescue", + ["-o".as_ref(), image.as_os_str(), image_dir.as_os_str()], + false, + )?; + + Ok(ImageBuilt(image)) +} diff --git a/xtask/src/build/mod.rs b/xtask/src/build/mod.rs index 28b08018..5064f0eb 100644 --- a/xtask/src/build/mod.rs +++ b/xtask/src/build/mod.rs @@ -10,6 +10,7 @@ use crate::{ error::Error, }; +pub mod i686; pub mod x86_64; mod cargo; @@ -40,6 +41,7 @@ pub struct ImageBuilt(pub PathBuf); pub enum AllBuilt { X86_64(ImageBuilt), AArch64(KernelProcessed, InitrdGenerated), + I686(ImageBuilt), } pub fn build_kernel_tools(env: &BuildEnv, _: AllOk) -> Result { @@ -81,13 +83,16 @@ pub fn build_all(env: BuildEnv) -> Result { // Kernel stuff let tools = build_kernel_tools(&env, check)?; let kernel = build_kernel(&env, check)?; - let kernel = generate_kernel_tables(&env.kernel_symbol_file, kernel, tools)?; - let modules = build_modules(&env, env.workspace_root.join("kernel/modules"))?; + let kernel = match env.arch { + Arch::i686 => KernelProcessed(kernel), + _ => generate_kernel_tables(&env.kernel_symbol_file, kernel, tools)?, + }; + // let modules = build_modules(&env, env.workspace_root.join("kernel/modules"))?; let mut install_extra = vec![]; - for module in modules { - install_extra.push((module.clone(), module.file_name().unwrap().into())); - } + // for module in modules { + // install_extra.push((module.clone(), module.file_name().unwrap().into())); + // } // Userspace stuff let initrd = userspace::build_initrd(&env, install_extra, check)?; @@ -96,6 +101,7 @@ pub fn build_all(env: BuildEnv) -> Result { let image = match env.arch { Arch::aarch64 => AllBuilt::AArch64(kernel, initrd), Arch::x86_64 => AllBuilt::X86_64(x86_64::build_image(&env, kernel, initrd)?), + Arch::i686 => AllBuilt::I686(i686::build_image(&env, kernel, initrd)?), }; Ok(image) diff --git a/xtask/src/build/userspace.rs b/xtask/src/build/userspace.rs index e00dfe4c..f479f240 100644 --- a/xtask/src/build/userspace.rs +++ b/xtask/src/build/userspace.rs @@ -32,7 +32,7 @@ const PROGRAMS: &[(&str, &str)] = &[ ("random", "bin/random"), ("view", "bin/view"), ("chmod", "bin/chmod"), - ("sha256sum", "bin/sha256sum"), + // ("sha256sum", "bin/sha256sum"), ("sysmon", "bin/sysmon"), // netutils ("netconf", "sbin/netconf"), @@ -47,14 +47,14 @@ const PROGRAMS: &[(&str, &str)] = &[ // red ("red", "bin/red"), // rdb - ("rdb", "bin/rdb"), - ("dyn-loader", "libexec/dyn-loader"), + // ("rdb", "bin/rdb"), + // ("dyn-loader", "libexec/dyn-loader"), ]; fn build_userspace(env: &BuildEnv, _: AllOk) -> Result<(), Error> { log::info!("Building userspace"); CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace"))?; - CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace/dynload-program"))?; + //CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace/dynload-program"))?; Ok(()) } @@ -95,25 +95,25 @@ fn build_rootfs, D: AsRef>( // TODO this is a temporary hack fs::create_dir_all(rootfs_dir.join("lib"))?; - util::copy_file( - env.workspace_root.join(format!( - "userspace/dynload-program/target/{}-unknown-yggdrasil/{}/dynload-program", - env.arch.name(), - env.profile.name() - )), - rootfs_dir.join("dynload-program"), - )?; - util::copy_file(format!( - "/home/alnyan/build/ygg/toolchain/build/host/stage1-std/{}-unknown-yggdrasil/release/libstd.so", - env.arch.name() - ), rootfs_dir.join("lib/libstd.so"))?; + // util::copy_file( + // env.workspace_root.join(format!( + // "userspace/dynload-program/target/{}-unknown-yggdrasil/{}/dynload-program", + // env.arch.name(), + // env.profile.name() + // )), + // rootfs_dir.join("dynload-program"), + // )?; + // util::copy_file(format!( + // "/home/alnyan/build/ygg/toolchain/build/host/stage1-std/{}-unknown-yggdrasil/release/libstd.so", + // env.arch.name() + // ), rootfs_dir.join("lib/libstd.so"))?; log::info!("Installing extras"); for (src, dst) in install_extra { util::copy_file(src, rootfs_dir.join(dst))?; } - util::copy_file(&env.kernel_symbol_file, rootfs_dir.join("kernel.sym"))?; + // util::copy_file(&env.kernel_symbol_file, rootfs_dir.join("kernel.sym"))?; // Copy /etc util::copy_dir_recursive(user_dir.join("etc"), rootfs_dir.join("etc"))?; diff --git a/xtask/src/check.rs b/xtask/src/check.rs index ca514728..1f9b9a90 100644 --- a/xtask/src/check.rs +++ b/xtask/src/check.rs @@ -44,6 +44,10 @@ fn check_commands_x86_64() -> Result { ]) } +fn check_commands_i686() -> Result { + check_command_list([("grub-mkrescue", "Install the `grub` package")]) +} + fn check_commands_aarch64() -> Result { check_command_list([ ("llvm-objcopy", "Install LLVM"), @@ -56,6 +60,7 @@ pub fn check_build_env(arch: Arch) -> Result { let commands = match arch { Arch::x86_64 => check_commands_x86_64()?, Arch::aarch64 => check_commands_aarch64()?, + Arch::i686 => check_commands_i686()?, }; Ok(AllOk(commands, user_toolchain)) } diff --git a/xtask/src/env.rs b/xtask/src/env.rs index 0d4ea804..e4286319 100644 --- a/xtask/src/env.rs +++ b/xtask/src/env.rs @@ -28,6 +28,7 @@ pub enum Arch { #[default] aarch64, x86_64, + i686 } #[derive(Debug)] @@ -107,6 +108,7 @@ impl Arch { match self { Self::aarch64 => "aarch64-unknown-qemu", Self::x86_64 => "x86_64-unknown-none", + Self::i686 => "i686-unknown-none" } } @@ -114,6 +116,7 @@ impl Arch { match self { Self::aarch64 => "aarch64-unknown-yggdrasil", Self::x86_64 => "x86_64-unknown-yggdrasil", + Self::i686 => "i686-unknown-yggdrasil" } } @@ -121,6 +124,7 @@ impl Arch { match self { Self::aarch64 => "aarch64", Self::x86_64 => "x86_64", + Self::i686 => "i686", } } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 37be4496..45424822 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,4 +1,4 @@ -#![deny(warnings)] +// #![deny(warnings)] use std::{fs, path::PathBuf, process::ExitCode}; diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index d7e8d819..5e07f7c3 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -6,7 +6,7 @@ use std::{ use qemu::{ aarch64, device::{QemuDevice, QemuNic, QemuSerialTarget}, - x86_64, Qemu, + i386, x86_64, Qemu, }; use crate::{ @@ -39,11 +39,19 @@ struct QemuAArch64MachineConfig { memory: usize, } +#[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "kebab-case", default)] +struct QemuI686MachineConfig { + enable_kvm: bool, + memory: usize, +} + #[derive(Debug, serde::Deserialize)] #[serde(default)] struct QemuMachineConfig { x86_64: QemuX86_64MachineConfig, aarch64: QemuAArch64MachineConfig, + i686: QemuI686MachineConfig, smp: usize, } @@ -59,11 +67,21 @@ impl Default for QemuMachineConfig { Self { x86_64: Default::default(), aarch64: Default::default(), + i686: Default::default(), smp: 4, } } } +impl Default for QemuI686MachineConfig { + fn default() -> Self { + Self { + enable_kvm: true, + memory: 256, + } + } +} + impl Default for QemuAArch64MachineConfig { fn default() -> Self { Self { memory: 512 } @@ -161,6 +179,31 @@ fn run_x86_64( Ok(qemu.into_command()) } +fn run_i686( + config: &QemuConfig, + qemu_bin: Option, + devices: Vec, + image: PathBuf, +) -> Result { + let mut qemu = Qemu::new_i386(); + if let Some(qemu_bin) = qemu_bin { + qemu.override_qemu(qemu_bin); + } + qemu.with_serial(QemuSerialTarget::MonStdio) + .with_cpu(i386::Cpu::Host { + enable_kvm: config.machine.i686.enable_kvm, + }) + .with_machine(i386::Machine::Q35) + .with_cdrom(&image) + .with_memory_megabytes(config.machine.i686.memory); + + for device in devices { + qemu.with_device(device); + } + + Ok(qemu.into_command()) +} + fn load_qemu_config>(path: P) -> Result { let path = path.as_ref(); @@ -208,6 +251,7 @@ pub fn run(env: BuildEnv, qemu: Option, extra_args: Vec) -> Res run_aarch64(&config, qemu, devices, kernel_bin, initrd)? } AllBuilt::X86_64(ImageBuilt(image)) => run_x86_64(&config, qemu, devices, image)?, + AllBuilt::I686(ImageBuilt(image)) => run_i686(&config, qemu, devices, image)?, }; command.args(extra_args);