maint: drop support for i686

This commit is contained in:
2025-07-16 20:19:40 +03:00
parent 223aeef10f
commit 6a723790de
32 changed files with 20 additions and 2992 deletions
Generated
-17
View File
@@ -1119,7 +1119,6 @@ dependencies = [
"cfg-if",
"kernel-arch-aarch64",
"kernel-arch-hosted",
"kernel-arch-i686",
"kernel-arch-interface",
"kernel-arch-riscv64",
"kernel-arch-x86_64",
@@ -1151,21 +1150,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "kernel-arch-i686"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"device-api",
"kernel-arch-interface",
"kernel-arch-x86",
"libk-mm-interface",
"log",
"static_assertions",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "kernel-arch-interface"
version = "0.1.0"
@@ -2980,7 +2964,6 @@ dependencies = [
"git-version",
"kernel-arch",
"kernel-arch-aarch64",
"kernel-arch-i686",
"kernel-arch-interface",
"kernel-arch-riscv64",
"kernel-arch-x86",
-5
View File
@@ -73,10 +73,6 @@ ygg_driver_net_igbe.path = "driver/net/igbe"
acpi.workspace = true
[target.'cfg(target_arch = "x86")'.dependencies]
kernel-arch-i686.workspace = true
kernel-arch-x86.workspace = true
[build-dependencies]
abi-generator.workspace = true
@@ -87,7 +83,6 @@ prettyplease = "0.2.15"
aarch64-cpu.workspace = true
device-tree.workspace = true
kernel-arch-x86_64.workspace = true
kernel-arch-i686.workspace = true
kernel-arch-x86.workspace = true
kernel-arch-aarch64.workspace = true
kernel-arch-riscv64.workspace = true
-3
View File
@@ -9,9 +9,6 @@ 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(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
kernel-arch-riscv64.path = "riscv64"
-16
View File
@@ -1,16 +0,0 @@
[package]
name = "kernel-arch-i686"
version = "0.1.0"
edition = "2021"
[dependencies]
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk-mm-interface.workspace = true
device-api = { workspace = true, features = ["derive"] }
kernel-arch-x86.workspace = true
bitflags.workspace = true
static_assertions.workspace = true
tock-registers.workspace = true
log.workspace = true
-116
View File
@@ -1,116 +0,0 @@
// 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:
// %esp + 4: argument
// %esp + 0: entry
xor %ecx, %ecx
xchg (%esp), %ecx
// 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
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
// Store stack to "from" context
mov %esp, (%ecx)
// Load stack from "to" context
mov (%eax), %esp
LOAD_TASK_STATE
ret
__i686_enter_task:
// %esp + 0: return
// %esp + 4: destination
// Switch to destination stack
mov 4(%esp), %eax
mov (%eax), %esp
LOAD_TASK_STATE
ret
__i686_switch_and_drop:
// %esp + 0: return
// %esp + 4: destination
// %esp + 8: thread to drop
mov 8(%esp), %ecx
mov 4(%esp), %eax
// Switch to stack
mov (%eax), %esp
LOAD_TASK_STATE
// TODO actually drop the thread
ret
-462
View File
@@ -1,462 +0,0 @@
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{StackBuilder, TaskContext, TaskFrame, UserContextInfo},
};
use kernel_arch_x86::registers::{FpuContext, CR3};
use libk_mm_interface::address::{AsPhysicalAddress, PhysicalAddress};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::{arch::SavedFrame, error::Error};
use crate::{
gdt::{self, TSS},
mem::KERNEL_TABLES,
};
#[allow(unused)]
#[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,
pub esp: u32,
pub ss: u32,
}
#[allow(unused)]
#[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,
}
#[allow(unused)]
#[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,
gs_base: usize,
}
#[allow(dead_code)]
pub struct TaskContextImpl<
K: KernelTableManager,
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
> {
inner: UnsafeCell<Inner>,
fpu_context: Option<UnsafeCell<FpuContext>>,
stack_base_phys: PhysicalAddress,
stack_size: usize,
cr3: u32,
tss_esp0: u32,
_pd: PhantomData<(K, PA)>,
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContextImpl<K, PA>
{
unsafe fn store_state(&self) {
if let Some(fpu) = self.fpu_context.as_ref() {
FpuContext::store(fpu.get());
}
}
unsafe fn load_state(&self) {
if let Some(fpu) = self.fpu_context.as_ref() {
FpuContext::restore(fpu.get());
}
gdt::set_gs_base((*self.inner.get()).gs_base);
TSS.esp0 = self.tss_esp0;
CR3.set(self.cr3 as _);
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContext<K, PA> for TaskContextImpl<K, PA>
{
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
const USER_STACK_EXTRA_ALIGN: usize = 0;
fn user(context: UserContextInfo) -> Result<Self, Error> {
const USER_TASK_PAGES: usize = 16;
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
let mut flags = 0x200;
if context.single_step {
flags |= 1 << 8;
}
stack.push(flags);
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;
let fpu_context = FpuContext::new(true);
Ok(Self {
inner: UnsafeCell::new(Inner {
sp,
gs_base: context.thread_pointer,
}),
fpu_context: Some(UnsafeCell::new(fpu_context)),
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<Self, yggdrasil_abi::error::Error> {
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::<K>();
let mut stack = StackBuilder::new(stack_base, KERNEL_TASK_PAGES * 0x1000);
// Entry and argument
stack.push(arg);
stack.push(entry as _);
// XXX
setup_common_context(&mut stack, __i686_task_enter_kernel as _);
let sp = stack.build();
let cr3 = unsafe {
KERNEL_TABLES
.lock()
.as_physical_address()
.try_into_u32()
.unwrap()
};
// TODO stack is leaked
Ok(Self {
inner: UnsafeCell::new(Inner { sp, gs_base: 0 }),
fpu_context: None,
stack_base_phys,
stack_size: KERNEL_TASK_PAGES * 0x1000,
tss_esp0: 0,
cr3,
_pd: PhantomData,
})
}
unsafe fn switch(&self, from: &Self) {
if core::ptr::addr_eq(self, from) {
return;
}
from.store_state();
self.load_state();
__i686_switch_task(self.inner.get(), from.inner.get());
}
unsafe fn enter(&self) -> ! {
self.load_state();
__i686_enter_task(self.inner.get())
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
self.load_state();
__i686_switch_and_drop(self.inner.get(), thread);
}
fn set_thread_pointer(&self, tp: usize) {
unsafe { (*self.inner.get()).gs_base = tp };
gdt::set_gs_base(tp);
}
fn align_stack_for_entry(sp: usize) -> usize {
(sp & !0xF) - 12
}
}
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) {
self.eax = saved.eax as _;
self.args[0] = saved.ebx as _;
self.args[1] = saved.ecx as _;
self.args[2] = saved.edx as _;
self.args[3] = saved.esi as _;
self.args[4] = saved.edi as _;
self.args[5] = saved.ebp as _;
self.eip = saved.user_ip;
self.esp = saved.user_sp;
self.eflags = saved.eflags;
}
fn user_sp(&self) -> usize {
todo!()
}
fn user_ip(&self) -> usize {
todo!()
}
fn argument(&self) -> u64 {
self.args[0] as _
}
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) {
if step {
self.eflags |= 1 << 8;
} else {
self.eflags &= !(1 << 8);
}
}
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 {
self.eip as _
}
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) {
if step {
self.eflags |= 1 << 8;
} else {
self.eflags &= !(1 << 8);
}
}
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));
-108
View File
@@ -1,108 +0,0 @@
use core::{cell::UnsafeCell, ptr::addr_of_mut};
use kernel_arch_interface::guard::IrqGuard;
pub use kernel_arch_x86::gdt::{Entry, Pointer};
use crate::ArchitectureImpl;
#[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;
pub static mut GDT: UnsafeCell<[Entry; 7]> = UnsafeCell::new([
Entry::NULL, // 0x00
Entry::RING0_CS32, // 0x08
Entry::RING0_DS32, // 0x10
Entry::RING3_CS32, // 0x1B
Entry::RING3_DS32, // 0x23
Entry::NULL, // 0x28, TSS
Entry::RING3_GS32, // 0x33, Task GS
]);
pub fn create_gdt() -> (&'static [Entry], &'static Tss) {
// Won't be deallocated, so leaks are not a concern
let tss = unsafe { &mut *addr_of_mut!(TSS) };
tss.ss0 = 0x10;
let tss_addr = (tss as *mut Tss).addr();
#[allow(static_mut_refs)]
let gdt = unsafe { GDT.get_mut() };
gdt[5] = Entry::tss(tss_addr as u32, (size_of::<Tss>() - 1) as u32);
(gdt, tss)
}
pub fn set_gs_base(gs_base: usize) {
let _guard = IrqGuard::<ArchitectureImpl>::acquire();
unsafe {
#[allow(static_mut_refs)]
GDT.get_mut()[6].set_base(gs_base);
core::arch::asm!("mov $0x33, %ax; mov %ax, %gs", out("ax") _, options(att_syntax, nostack));
}
}
-142
View File
@@ -1,142 +0,0 @@
#![feature(never_type, naked_functions, trace_macros)]
#![no_std]
extern crate alloc;
use core::ptr::null_mut;
use alloc::vec::Vec;
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuData, CpuImpl, IpiQueue},
task::Scheduler,
Architecture,
};
pub mod context;
pub mod gdt;
pub mod mem;
pub use context::TaskContextImpl;
use kernel_arch_x86::cpuid::CpuFeatures;
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
pub struct ArchitectureImpl;
#[repr(C)]
pub struct PerCpuData {
pub available_features: CpuFeatures,
pub enabled_features: CpuFeatures,
}
impl CpuData for PerCpuData {}
static mut CPU: *mut () = null_mut();
#[naked]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::naked_asm!(
r#"
1:
nop
jmp 1b
"#,
options(att_syntax)
);
}
}
impl Architecture for ArchitectureImpl {
type PerCpuData = PerCpuData;
type CpuFeatures = CpuFeatures;
type BreakpointType = u8;
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
use alloc::boxed::Box;
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::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<IpiQueue<Self>>) {}
fn local_cpu() -> *mut () {
unsafe { CPU }
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
0
}
unsafe fn set_local_cpu(cpu: *mut ()) {
CPU = cpu;
}
fn cpu_count() -> usize {
1
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
None
}
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
None
}
fn ipi_queue(_cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
None
}
fn idle_task() -> extern "C" fn(usize) -> ! {
idle_task
}
fn halt() -> ! {
loop {
unsafe {
core::arch::asm!("cli; hlt");
}
}
}
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
Some(&cpu.available_features)
}
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
Some(&cpu.enabled_features)
}
}
-141
View File
@@ -1,141 +0,0 @@
use kernel_arch_interface::{sync::IrqSafeSpinlock, KERNEL_VIRT_OFFSET};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel, KernelImageObject};
use yggdrasil_abi::error::Error;
use crate::{
mem::{flush_tlb_entry, table::PageAttributes},
ArchitectureImpl,
};
use super::{
table::{PageEntry, PageTable, L0, L3},
KERNEL_TABLES,
};
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 DYNAMIC_MAP_OFFSET: usize = (KERNEL_SPLIT_L0 + FIXED_MAP_COUNT) << L0::SHIFT;
pub const MAX_FIXED_PHYSICAL: PhysicalAddress =
PhysicalAddress::from_u64((FIXED_MAP_COUNT as u64) << 22);
#[repr(C)]
pub struct FixedTables {
pub l0: KernelL0,
pub dynamic: IrqSafeSpinlock<ArchitectureImpl, KernelDynamic>,
}
#[repr(C, align(0x1000))]
pub struct KernelL0 {
pub lower: [PageEntry<L0>; KERNEL_SPLIT_L0],
pub kernel: [PageEntry<L0>; FIXED_MAP_COUNT],
pub dynamic: [PageEntry<L0>; DYNAMIC_MAP_COUNT],
}
#[allow(unused)]
pub struct KernelDynamic {
pub l3s: [KernelImageObject<PageTable<L3>>; DYNAMIC_MAP_COUNT],
free: usize,
}
impl FixedTables {
pub const fn zeroed() -> Self {
Self {
l0: KernelL0::zeroed(),
dynamic: IrqSafeSpinlock::new(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 {
todo!()
}
}
pub fn physicalize(&mut self, address: usize) -> Option<PhysicalAddress> {
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 {
todo!()
}
}
pub fn map_dynamic_memory(&mut self, base: u64, page_count: usize) -> Result<usize, Error> {
self.dynamic.lock().map(base, page_count)
}
}
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: [const { unsafe { KernelImageObject::new(PageTable::zeroed()) } };
DYNAMIC_MAP_COUNT],
free: DYNAMIC_MAP_COUNT * 1024,
}
}
fn map(&mut self, base: u64, page_count: usize) -> Result<usize, Error> {
if page_count > self.free {
return Err(Error::OutOfMemory);
}
'l0: for i in 0..DYNAMIC_MAP_COUNT * 1024 - page_count {
for j in 0..page_count {
let entry = self.entry(i + j);
if entry.is_present() {
continue 'l0;
}
}
self.free -= page_count;
for j in 0..page_count {
let address = PhysicalAddress::from_u64(base + ((j as u64) << L3::SHIFT));
*self.entry_mut(i + j) = PageEntry::page(address, PageAttributes::WRITABLE);
unsafe {
flush_tlb_entry(DYNAMIC_MAP_OFFSET + ((i + j) << L3::SHIFT));
}
}
let addr = DYNAMIC_MAP_OFFSET + (i << L3::SHIFT);
return Ok(addr);
}
Err(Error::OutOfMemory)
}
fn entry(&self, index: usize) -> &PageEntry<L3> {
&self.l3s[index / 1024][index % 1024]
}
fn entry_mut(&mut self, index: usize) -> &mut PageEntry<L3> {
&mut self.l3s[index / 1024][index % 1024]
}
}
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
let tables = KERNEL_TABLES.lock();
for (i, entry) in tables.l0.kernel.iter().enumerate() {
dst[i + KERNEL_SPLIT_L0] = *entry;
}
for (i, entry) in tables.l0.dynamic.iter().enumerate() {
dst[i + KERNEL_SPLIT_L0 + FIXED_MAP_COUNT] = *entry;
}
}
-131
View File
@@ -1,131 +0,0 @@
use fixed::FixedTables;
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock, KERNEL_VIRT_OFFSET,
};
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
table::{page_count, EntryLevel},
};
use table::{PageAttributes, PageEntry, L0, L3};
use yggdrasil_abi::error::Error;
pub mod fixed;
pub mod process;
pub mod table;
pub use process::ProcessAddressSpaceImpl;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
split_spinlock! {
use libk_mm_interface::KernelImageObject;
use crate::mem::FixedTables;
use crate::ArchitectureImpl;
#[link_section = ".data.tables"]
static KERNEL_TABLES: KernelImageObject<FixedTables> = unsafe {
KernelImageObject::new(FixedTables::zeroed())
};
}
impl KernelTableManager for KernelTableManagerImpl {
unsafe fn map_device_pages(
base: u64,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
// TODO page align up
let offset = (base & 0xFFF) as usize;
let base = base & !0xFFF;
let end = (base + count as u64 + 0xFFF) & !0xFFF;
// assert_eq!(base & 0xFFF, 0);
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 {
assert_eq!(base & 0xFFF, 0);
log::info!("map_device_pages({:#x}, {})", base, count);
let page_count = page_count::<L3>(count);
let virt = KERNEL_TABLES.lock().map_dynamic_memory(base, page_count)?;
Ok(RawDeviceMemoryMapping::from_raw_parts(
virt + offset,
virt,
page_count,
0,
))
}
}
unsafe fn unmap_device_pages(_mapping: &RawDeviceMemoryMapping<Self>) {
// todo!()
}
fn virtualize(phys: u64) -> usize {
KERNEL_TABLES
.lock()
.virtualize(PhysicalAddress::from_u64(phys))
}
fn physicalize(virt: usize) -> u64 {
KERNEL_TABLES
.lock()
.physicalize(virt)
.expect("Invalid virtual address")
.into_u64()
}
unsafe fn unmap_physical_address(virt: usize) {
if virt < KERNEL_VIRT_OFFSET {
panic!("Invalid 'virtualized' address: {:#x}", virt);
}
let virt = virt - KERNEL_VIRT_OFFSET;
if virt >= fixed::FIXED_MAP_COUNT << L0::SHIFT {
todo!()
}
}
}
/// Sets up fixed MMU translation tables.
///
/// # Safety
///
/// Only meant to be called once during early OS init.
pub unsafe fn init_fixed_tables() {
let mut tables = KERNEL_TABLES.lock();
// Unmap lower stuff
for (i, entry) in tables.l0.lower.iter_mut().enumerate() {
*entry = PageEntry::INVALID;
flush_tlb_entry(i << 22);
}
// Map the rest of fixed translation
for (i, entry) in tables.l0.kernel.iter_mut().enumerate() {
let virt = KERNEL_VIRT_OFFSET + (i << L0::SHIFT);
let phys = (i << L0::SHIFT) as u32;
*entry = PageEntry::block(PhysicalAddress::from_u32(phys), PageAttributes::WRITABLE);
flush_tlb_entry(virt);
}
let dynamic_len = tables.l0.dynamic.len();
for i in 0..dynamic_len {
let phys = tables.dynamic.lock().l3s[i].as_physical_address();
tables.l0.dynamic[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
}
}
/// # 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));
}
-137
View File
@@ -1,137 +0,0 @@
use core::marker::PhantomData;
use kernel_arch_interface::KERNEL_VIRT_OFFSET;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::ProcessAddressSpaceManager,
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
};
use yggdrasil_abi::error::Error;
use crate::{mem::flush_tlb_entry, KernelTableManagerImpl};
use super::{
fixed::{clone_kernel_tables, KERNEL_SPLIT_L0},
table::{PageEntry, PageTable, L0, L3},
};
#[repr(C)]
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
l0: PhysicalRefMut<'static, PageTable<L0>, KernelTableManagerImpl>,
_alloc: PhantomData<TA>,
}
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
const UPPER_LIMIT_PFN: usize = KERNEL_VIRT_OFFSET >> L3::SHIFT;
const LOWER_LIMIT_PFN: usize = 32;
fn new() -> Result<Self, Error> {
let mut l0 = unsafe {
PhysicalRefMut::<'static, PageTable<L0>, 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) {
self.l0.drop_range::<TA>(0..KERNEL_SPLIT_L0);
}
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<PhysicalAddress, Error> {
self.pop_l3_entry(address)
}
fn as_address_with_asid(&self) -> (u64, u64) {
(unsafe { self.l0.as_physical_address().into_u64() }, 0)
}
}
impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
// Write a single 4KiB entry
fn write_l3_entry(
&mut self,
virt: usize,
entry: PageEntry<L3>,
overwrite: bool,
) -> Result<(), Error> {
let l0i = virt.page_index::<L0>();
let l3i = virt.page_index::<L3>();
let mut l3 = self.l0.get_mut_or_alloc::<TA>(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<PhysicalAddress, Error> {
let l0i = virt.page_index::<L0>();
let l3i = virt.page_index::<L3>();
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::<L0>();
let l3i = virt.page_index::<L3>();
let l3 = self.l0.get(l0i)?;
let page = l3[l3i].as_page()?;
Some((page.add(virt & 0xFFF), l3[l3i].attributes().into()))
}
}
impl<TA: TableAllocator> Drop for ProcessAddressSpaceImpl<TA> {
fn drop(&mut self) {
// SAFETY: with safe usage of the ProcessAddressSpaceImpl, clearing and dropping
// is safe, no one refers to the memory
unsafe {
self.clear();
let l0_phys = self.l0.as_physical_address();
TA::free_page_table(l0_phys);
}
}
}
-256
View File
@@ -1,256 +0,0 @@
use core::{
marker::PhantomData,
ops::{Index, IndexMut, Range},
};
use bitflags::bitflags;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
table::{
EntryLevel, EntryLevelDrop, 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<L: EntryLevel>(u32, PhantomData<L>);
#[derive(Clone, Copy, Debug)]
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
data: [PageEntry<L>; 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<L3> {
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<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0 {
Some(PhysicalAddress::from_u32(self.0 & !0xFFF))
} else {
None
}
}
}
impl PageEntry<L0> {
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<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some(PhysicalAddress::from_u32(self.0 & !0xFFF))
} else {
None
}
}
}
impl<L: EntryLevel> PageEntry<L> {
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<L: EntryLevel> PageTable<L> {
pub const fn zeroed() -> Self {
Self {
data: [PageEntry::INVALID; 1024],
}
}
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, 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)
}
/// Recursively clears and deallocates the translation table.
///
/// # Safety
///
/// The caller must ensure the table is no longer in use and is not referenced anymore.
pub unsafe fn free<TA: TableAllocator>(this: PhysicalRefMut<Self, KernelTableManagerImpl>) {
let physical = this.as_physical_address();
TA::free_page_table(physical);
}
}
impl NextPageTable for PageTable<L0> {
type NextLevel = PageTable<L3>;
type TableRef = PhysicalRef<'static, Self::NextLevel, KernelTableManagerImpl>;
type TableRefMut = PhysicalRefMut<'static, Self::NextLevel, KernelTableManagerImpl>;
fn get(&self, index: usize) -> Option<Self::TableRef> {
self[index]
.as_table()
.map(|addr| unsafe { PhysicalRef::map(addr) })
}
fn get_mut(&mut self, index: usize) -> Option<Self::TableRefMut> {
self[index]
.as_table()
.map(|addr| unsafe { PhysicalRefMut::map(addr) })
}
fn get_mut_or_alloc<TA: TableAllocator>(
&mut self,
index: usize,
) -> Result<Self::TableRefMut, Error> {
let entry = self[index];
if let Some(table) = entry.as_table() {
Ok(unsafe { PhysicalRefMut::map(table) })
} else {
let table = PageTable::new_zeroed::<TA>()?;
self[index] = PageEntry::<L0>::table(
unsafe { table.as_physical_address() },
PageAttributes::WRITABLE | PageAttributes::USER,
);
Ok(table)
}
}
}
impl<L: EntryLevel> Index<usize> for PageTable<L> {
type Output = PageEntry<L>;
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.data[index]
}
}
impl EntryLevelDrop for PageTable<L3> {
const FULL_RANGE: Range<usize> = 0..1024;
unsafe fn drop_range<TA: TableAllocator>(&mut self, _range: Range<usize>) {}
}
impl EntryLevelDrop for PageTable<L0> {
const FULL_RANGE: Range<usize> = 0..1024;
unsafe fn drop_range<TA: TableAllocator>(&mut self, range: Range<usize>) {
for index in range {
let entry = self[index];
if let Some(table) = entry.as_table() {
let mut table_ref: PhysicalRefMut<PageTable<L3>, KernelTableManagerImpl> =
PhysicalRefMut::map(table);
table_ref.drop_all::<TA>();
TA::free_page_table(table);
} else if entry.is_present() {
// Memory must've been cleared beforehand, so no non-table entries must be present
panic!(
"Expected a table containing only tables, got table[{}] = {:#x?}",
index, entry.0
);
}
self[index] = PageEntry::INVALID;
}
}
}
impl From<MapAttributes> 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<PageAttributes> 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
}
}
-2
View File
@@ -26,8 +26,6 @@ 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 if #[cfg(target_arch = "riscv64")] {
extern crate kernel_arch_riscv64 as imp;
} else {
-43
View File
@@ -1,43 +0,0 @@
use core::arch::global_asm;
use kernel_arch_i686::mem::KERNEL_TABLES;
use libk_mm::{address::PhysicalAddress, pointer::PhysicalRef};
use multiboot::MultibootInfo;
use crate::{kernel_main, mem::KERNEL_VIRT_OFFSET};
use super::PLATFORM;
const BOOT_STACK_SIZE: usize = 64 * 1024;
pub mod multiboot;
#[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::<MultibootInfo>::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)
);
-56
View File
@@ -1,56 +0,0 @@
.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}
-118
View File
@@ -1,118 +0,0 @@
use core::ffi::CStr;
use libk_mm::{
address::{PhysicalAddress, Virtualize},
phys::PhysicalMemoryRegion,
};
use crate::arch::x86::InitrdSource;
#[derive(Clone, Copy, Debug)]
#[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, 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 Iterator for MultibootMemoryMapIter<'_> {
type Item = PhysicalMemoryRegion;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.pos + size_of::<MultibootMemoryMapEntry>()
>= 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::<u32>();
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) }
}
pub fn cmdline(&self) -> &str {
if self.cmdline == 0 {
""
} else {
let address = PhysicalAddress::from_u32(self.cmdline).virtualize();
let data = unsafe { CStr::from_ptr(core::ptr::with_exposed_provenance(address)) };
data.to_str().unwrap_or("")
}
}
}
impl MultibootMemoryMapEntry {
pub fn is_available(&self) -> bool {
self.ty == 1
}
}
impl InitrdSource for MultibootModuleEntry {
fn start(&self) -> PhysicalAddress {
PhysicalAddress::from_u32(self.mod_start)
}
fn end(&self) -> PhysicalAddress {
PhysicalAddress::from_u32(self.mod_end)
}
}
-311
View File
@@ -1,311 +0,0 @@
use core::arch::global_asm;
use abi::{primitive_enum, process::Signal, SyscallFunction};
use kernel_arch_i686::context::{ExceptionFrame, SyscallFrame};
use kernel_arch_x86::registers::{CR2, CR3};
use libk::task::thread::Thread;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use tock_registers::interfaces::Readable;
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 IDT: IrqSafeRwLock<[Entry; SIZE]> = IrqSafeRwLock::new([Entry::NULL; SIZE]);
fn dump_user_exception(kind: ExceptionKind, frame: &ExceptionFrame) {
let thread = Thread::current();
log::warn!("{:?} in {} ({:?})", kind, thread.id, *thread.name.read());
log::warn!("ip = {:02x}:{:08x}", frame.cs, frame.eip);
log::warn!("sp = {:02x}:{:08x}", frame.ss, frame.esp);
log::warn!("cr3 = {:#010x}", CR3.get());
if kind == ExceptionKind::PageFault {
log::warn!("cr2 = {:#010x}", CR2.get());
}
}
fn kernel_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) -> ! {
let cr3 = CR3.get();
log::error!("{:?} in KERNEL, frame {:p}, cr3 = {:#x}", kind, frame, cr3);
if kind == ExceptionKind::PageFault {
let cr2 = CR2.get();
log::error!("cr2 = {:#x}", cr2);
}
log::error!("CS:EIP = {:02x}:{:08x}", frame.cs, frame.eip);
panic!("Irrecoverable exception")
}
fn user_exception_inner(kind: ExceptionKind, frame: &mut ExceptionFrame) {
let thread = Thread::current();
let dump = match kind {
ExceptionKind::PageFault => {
thread.raise_signal(Signal::MemoryAccessViolation);
true
}
ExceptionKind::GeneralProtectionFault => {
if thread.handle_breakpoint(frame) {
false
} else {
thread.raise_signal(Signal::MemoryAccessViolation);
true
}
}
ExceptionKind::InvalidOpcode => {
thread.raise_signal(Signal::Aborted);
true
}
ExceptionKind::Debug => {
if thread.handle_single_step(frame) {
false
} else {
thread.raise_signal(Signal::Aborted);
true
}
}
_ => {
log::warn!("No handler for exception: {:?}", kind);
thread.raise_signal(Signal::Aborted);
true
}
};
if dump {
dump_user_exception(kind, frame);
}
}
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 };
if frame.eax == usize::from(SyscallFunction::ExitSignal) {
unsafe {
syscall::handle_signal_exit(frame);
return;
}
}
if frame.eax == usize::from(SyscallFunction::Fork) {
todo!("Implement fork()")
// unsafe {
// Process::raw_fork(frame);
// return;
// }
}
// 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) };
}
}
/// Initializes interrupt descriptor table with exception, interrupt and system call vectors.
///
/// # Safety
///
/// Only meant to be called once during OS init.
pub unsafe fn init_exceptions() {
extern "C" {
static __i686_exception_vectors: [usize; 32];
fn __i686_dummy_vector();
fn __i686_syscall_vector();
}
let mut idt = IDT.write();
for (i, &entry) in __i686_exception_vectors.iter().enumerate() {
idt[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32);
}
for entry in idt.iter_mut().skip(32) {
*entry = 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::<Entry>()) as u16 - 1,
offset: idt.as_ptr().addr(),
};
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)
);
-221
View File
@@ -1,221 +0,0 @@
#![allow(missing_docs)]
use core::sync::atomic::{AtomicBool, Ordering};
use abi::error::Error;
use alloc::sync::Arc;
use boot::multiboot::MultibootInfo;
use device_api::{
interrupt::{IpiDeliveryTarget, IpiMessage},
ResetDevice,
};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_i686::{gdt, mem::table::L3, PerCpuData};
use kernel_arch_x86::cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures, ExtEdxFeatures};
use libk::{
arch::Cpu,
config, debug,
device::{
display::{
console::{add_console_autoflush, ConsoleWrapper},
DriverFlags,
},
manager::DEVICE_REGISTRY,
},
task::runtime,
vfs::{Terminal, TerminalInput},
};
use libk_mm::{
address::PhysicalAddress,
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
};
use peripherals::textfb::TextFramebuffer;
use crate::{arch::x86, util::call_init_array};
use super::Platform;
mod boot;
pub mod exception;
mod peripherals;
pub struct I686;
static SWITCH_TO_GRAPHIC: AtomicBool = AtomicBool::new(false);
pub static PLATFORM: I686 = I686;
impl Platform for I686 {
const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
type L3 = L3;
unsafe fn reset(&self) -> ! {
ArchitectureImpl::halt();
}
unsafe fn send_ipi(&self, _target: IpiDeliveryTarget, _msg: IpiMessage) -> Result<bool, Error> {
Ok(false)
}
unsafe fn start_application_processors(&self) {
// No APs in i686, go get a better CPU
}
fn register_reset_device(&self, _reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
unimplemented!()
}
}
impl I686 {
unsafe fn init_memory_management(&self, multiboot_info: &MultibootInfo) -> Result<(), Error> {
reserve_region(
"lowmem",
PhysicalMemoryRegion {
base: PhysicalAddress::ZERO,
size: 1024 * 1024,
},
);
let modules = multiboot_info.modules();
if !modules.is_empty() {
let initrd = &modules[0];
x86::set_initrd(initrd, true);
}
// Initialize physical memory
phys::init_from_iter(multiboot_info.memory_map_iter(), |_, _, _| Ok(()))?;
Ok(())
}
unsafe fn init_cpu(&self) -> Result<(), Error> {
init_gdt();
exception::init_exceptions();
let (have_features, will_features) = cpuid::setup_features(
CpuFeatures {
ecx: EcxFeatures::SSE3 | EcxFeatures::XSAVE | EcxFeatures::OSXSAVE,
edx: EdxFeatures::SSE2 | EdxFeatures::FXSR,
ext_edx: ExtEdxFeatures::empty(),
},
CpuFeatures {
ecx: EcxFeatures::empty(),
edx: EdxFeatures::FPU | EdxFeatures::SSE | EdxFeatures::PSE,
ext_edx: ExtEdxFeatures::empty(),
},
);
let will_features = will_features.expect("Could not enable needed CPU features");
let cpu_data = PerCpuData {
available_features: have_features,
enabled_features: will_features,
};
Cpu::init_local(Some(0), cpu_data);
Ok(())
}
unsafe fn init_framebuffer(&self) -> Result<(), Error> {
DEVICE_REGISTRY.display.set_callback(|device, _| {
log::info!("Display registered: {:?}", device.display_name());
if device
.driver_flags()
.contains(DriverFlags::REPLACES_BOOT_FRAMEBUFFER)
{
SWITCH_TO_GRAPHIC.store(true, Ordering::Release);
}
});
// Setup text framebuffer
// TODO check if video mode is set from boot info
let textfb = Arc::new(TextFramebuffer::new(
PhysicalAddress::from_u32(0xB8000),
80,
25,
)?);
debug::add_sink(textfb.clone(), config::get().debug.display_level);
add_console_autoflush(textfb.clone());
let textfb_console = Arc::new(Terminal::from_parts(
Default::default(),
TerminalInput::with_capacity(256)?,
ConsoleWrapper(textfb),
));
let keyboard_input = ygg_driver_input::setup();
runtime::spawn(
textfb_console
.clone()
.consume_keyboard(keyboard_input, Some(&SWITCH_TO_GRAPHIC)),
)
.ok();
DEVICE_REGISTRY.terminal.register(textfb_console).ok();
Ok(())
}
unsafe fn init_platform(&'static self, multiboot_info: &MultibootInfo) -> Result<(), Error> {
self.init_memory_management(multiboot_info)
.expect("Could not initialize memory management");
// Set kernel commandline
let cmdline = multiboot_info.cmdline();
self.init_cpu().expect("Could not initialize CPU");
let early = x86::init_platform_early(cmdline)?;
if !config::get().x86.disable_boot_fb {
if let Err(error) = self.init_framebuffer() {
log::error!("Could not initialize boot framebuffer: {error:?}");
}
}
x86::add_legacy_pci();
call_init_array();
x86::init_platform_devices(early);
Ok(())
}
}
unsafe fn load_gdt(gdt: &'static [gdt::Entry]) {
let gdt_addr = gdt.as_ptr().addr();
let gdtr = gdt::Pointer {
limit: size_of_val(gdt) 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)
);
}
/// # Safety
///
/// Only meant to be called during early init.
unsafe fn init_gdt() -> usize {
let (gdt, tss) = gdt::create_gdt();
load_gdt(gdt);
(tss as *const gdt::Tss).addr()
}
-1
View File
@@ -1 +0,0 @@
pub mod textfb;
-155
View File
@@ -1,155 +0,0 @@
use abi::error::Error;
use libk::{
debug::DebugSink,
device::display::console::{
Attributes, ColorAttribute, ConsoleBuffer, ConsoleChar, ConsoleState, DisplayConsole,
},
};
use libk_mm::{
address::PhysicalAddress,
device::{DeviceMemoryAttributes, DeviceMemoryIoMut},
};
use libk_util::sync::IrqSafeSpinlock;
use kernel_arch_x86::intrinsics::{io_wait, IoPort, IoPortAccess};
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Character {
pub char: u8,
pub attr: u8,
}
struct Cursor {
port0: IoPort<u8>,
port1: IoPort<u8>,
last_row: u8,
last_col: u8,
}
struct Inner {
data: DeviceMemoryIoMut<'static, [Character]>,
cursor: Cursor,
}
pub struct TextFramebuffer {
state: IrqSafeSpinlock<ConsoleState>,
inner: IrqSafeSpinlock<Inner>,
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<Self, Error> {
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<ConsoleState> {
&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.height, self.width)
}
}
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,
}
}
unsafe impl Sync for TextFramebuffer {}
unsafe impl Send for TextFramebuffer {}
-173
View File
@@ -1,173 +0,0 @@
// 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
-5
View File
@@ -23,11 +23,6 @@ pub mod x86_64;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub use x86_64::{PLATFORM, X86_64 as PlatformImpl};
#[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(any(target_arch = "riscv64", rust_analyzer))]
pub mod riscv64;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
-221
View File
@@ -1,221 +0,0 @@
//! Intel 8259 interrupt controller
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqOptions, IrqVector,
},
};
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
#[cfg(any(target_arch = "x86", rust_analyzer))]
use crate::arch::i686::exception;
#[cfg(any(target_arch = "x86", rust_analyzer))]
use kernel_arch_i686::context::InterruptFrame;
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<u8>,
master_data: IoPort<u8>,
slave_cmd: IoPort<u8>,
slave_data: IoPort<u8>,
}
pub struct I8259 {
inner: IrqSafeSpinlock<Inner>,
table: IrqSafeSpinlock<FixedInterruptTable<15>>,
i8253: OneTimeInit<Arc<I8253>>,
}
impl Device for I8259 {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> 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!() };
log::debug!("Enable IRQ#{}", irq);
let inner = self.inner.lock();
let port = if irq < 8 {
&inner.master_data
} else {
&inner.slave_data
};
let mask = port.read();
port.write(mask & !(1 << (irq % 8)));
Ok(())
}
fn register_irq(
&self,
irq: Irq,
_options: IrqOptions,
handler: Arc<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);
self.i8253.get().clone().irq_handler_fastpath();
} else if index < 16 {
// Rest of IRQs
self.eoi(index);
let table = self.table.lock();
let vector = IrqVector::Irq(Irq::External(index as u32));
if let Some(handler) = table.handler(index - 1) {
handler.clone().handle_irq(vector);
}
}
}
}
impl I8259 {
const fn new_inner() -> Inner {
Inner {
master_cmd: IoPort::new(0x20),
master_data: IoPort::new(0x21),
slave_cmd: IoPort::new(0xA0),
slave_data: IoPort::new(0xA1),
}
}
const fn new() -> Self {
Self {
inner: IrqSafeSpinlock::new(Self::new_inner()),
table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
i8253: OneTimeInit::new(),
}
}
pub fn setup() -> Result<Arc<Self>, Error> {
let this = Arc::new(Self::new());
I8259.init(this.clone());
Ok(this)
}
pub fn set_i8253(&self, i8253: Arc<I8253>) {
self.i8253.init(i8253);
}
pub fn disable(&self) {
let inner = self.inner.lock();
log::info!("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 (except IRQ#2 on master, used for cascading)
inner.master_data.write(!(1 << 2));
inner.slave_data.write(0xFF);
inner.master_cmd.write(0x20);
inner.slave_cmd.write(0x20);
}
pub fn eoi(&self, index: usize) {
let inner = self.inner.lock();
inner.master_cmd.write(0x20);
if index >= 8 {
inner.slave_cmd.write(0x20);
}
}
}
static I8259: OneTimeInit<Arc<I8259>> = OneTimeInit::new();
#[cfg(any(target_arch = "x86", rust_analyzer))]
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,
);
}
}
#[cfg(any(target_arch = "x86", rust_analyzer))]
extern "C" fn irq_handler(frame: *mut InterruptFrame) {
use libk::task::thread::Thread;
let frame = unsafe { &mut *frame };
I8259.get().handle_specific_irq(frame.irq_number as _);
if let Some(thread) = Thread::get_current() {
unsafe { thread.handle_pending_signals(frame) };
}
}
#[cfg(any(target_arch = "x86", rust_analyzer))]
core::arch::global_asm!(include_str!("i8259_vectors.S"), irq_handler = sym irq_handler, options(att_syntax));
@@ -1,85 +0,0 @@
.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
-1
View File
@@ -1,5 +1,4 @@
pub mod i8253;
pub mod i8259;
pub mod ps2;
pub mod rtc;
pub mod serial;
-4
View File
@@ -56,10 +56,6 @@ pub const fn arch_str() -> &'static str {
{
"riscv64"
}
#[cfg(target_arch = "x86")]
{
"i686"
}
}
/// Returns a generic machine description string
+1 -4
View File
@@ -85,11 +85,8 @@ impl<'e> CargoBuilder<'e> {
.display(),
env.arch
);
let mut build_std_features =
let build_std_features =
"compiler-builtins-mangled-names,compiler-builtins-mem".to_owned();
if env.arch == Arch::i686 {
build_std_features += ",compiler-builtins-no-f16-f128";
}
command
.arg(arg)
-36
View File
@@ -1,36 +0,0 @@
use std::fs;
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<ImageBuilt, Error> {
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))
}
-2
View File
@@ -11,7 +11,6 @@ use crate::{
error::Error,
};
pub mod i686;
pub mod x86_64;
pub mod c;
@@ -131,7 +130,6 @@ pub fn build_all(env: &BuildEnv) -> Result<AllBuilt, Error> {
Arch::riscv64 => AllBuilt::Riscv64(make_kernel_bin(env, kernel, check)?, initrd),
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)
+17 -8
View File
@@ -49,17 +49,26 @@ fn configure<P: AsRef<Path>>(env: &BuildEnv, config_toml_path: P) -> Result<(),
target: vec![
env.host_triple.clone(),
"x86_64-unknown-yggdrasil".into(),
"i686-unknown-yggdrasil".into(),
"riscv64-unknown-yggdrasil".into(),
"aarch64-unknown-yggdrasil".into(),
],
},
target: HashMap::from_iter([(
"aarch64-unknown-yggdrasil".into(),
RustTargetConfig {
cc: Some("aarch64-linux-gnu-gcc".into()),
cxx: Some("aarch64-linux-gnu-g++".into()),
},
)]),
target: HashMap::from_iter([
(
"aarch64-unknown-yggdrasil".into(),
RustTargetConfig {
cc: Some("aarch64-linux-gnu-gcc".into()),
cxx: Some("aarch64-linux-gnu-g++".into()),
},
),
(
"riscv64-unknown-yggdrasil".into(),
RustTargetConfig {
cc: Some("clang".into()),
cxx: Some("clang++".into()),
},
),
]),
rust: RustRustConfig { incremental: true },
};
-5
View File
@@ -45,10 +45,6 @@ fn check_commands_x86_64() -> Result<CommandsOk, Error> {
])
}
fn check_commands_i686() -> Result<CommandsOk, Error> {
check_command_list([("grub-mkrescue", "Install the `grub` package")])
}
fn check_commands_aarch64() -> Result<CommandsOk, Error> {
check_command_list([
("llvm-objcopy", "Install LLVM"),
@@ -66,7 +62,6 @@ pub fn check_build_env(arch: Arch) -> Result<AllOk, Error> {
Arch::x86_64 => check_commands_x86_64()?,
Arch::aarch64 => check_commands_aarch64()?,
Arch::riscv64 => check_commands_riscv64()?,
Arch::i686 => check_commands_i686()?,
};
Ok(AllOk(commands, user_toolchain))
}
+2 -7
View File
@@ -71,7 +71,6 @@ pub enum Arch {
aarch64,
riscv64,
x86_64,
i686,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, ValueEnum)]
@@ -143,7 +142,6 @@ impl BuildEnv {
(Arch::riscv64, Board::jh7110) => "riscv64-unknown-jh7110",
(Arch::aarch64, Board::raspi4b) => "aarch64-unknown-raspi4b",
(Arch::x86_64, Board::default) => "x86_64-unknown-none",
(Arch::i686, Board::default) => "i686-unknown-none",
_ => {
log::error!("Invalid arch/board combination: {arch:?}/{board:?}");
panic!();
@@ -152,7 +150,7 @@ impl BuildEnv {
let kernel_linker_script = match arch {
Arch::aarch64 => format!("arm/{kernel_triple}.ld"),
Arch::riscv64 => format!("riscv/{kernel_triple}.ld"),
Arch::i686 | Arch::x86_64 => format!("x86/{kernel_triple}.ld"),
Arch::x86_64 => format!("x86/{kernel_triple}.ld"),
};
let kernel_output_dir =
workspace_root.join(format!("target/{}/{}", kernel_triple, profile.dir()));
@@ -210,7 +208,6 @@ impl XTaskConfig {
Arch::riscv64 => &self.target.riscv64.components,
Arch::aarch64 => &self.target.aarch64.components,
Arch::x86_64 => &self.target.x86_64.components,
Arch::i686 => &self.target.i686.components,
}
}
}
@@ -230,7 +227,7 @@ impl Profile {
impl Arch {
pub fn all() -> impl Iterator<Item = Self> {
[Self::aarch64, Self::x86_64, Self::i686, Self::riscv64].into_iter()
[Self::aarch64, Self::x86_64, Self::riscv64].into_iter()
}
pub fn user_triple(&self) -> &str {
@@ -238,7 +235,6 @@ impl Arch {
Self::riscv64 => "riscv64-unknown-yggdrasil",
Self::aarch64 => "aarch64-unknown-yggdrasil",
Self::x86_64 => "x86_64-unknown-yggdrasil",
Self::i686 => "i686-unknown-yggdrasil",
}
}
@@ -247,7 +243,6 @@ impl Arch {
Self::riscv64 => "riscv64",
Self::aarch64 => "aarch64",
Self::x86_64 => "x86_64",
Self::i686 => "i686",
}
}
}