maint: drop support for i686
This commit is contained in:
Generated
-17
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
@@ -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}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
@@ -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 +0,0 @@
|
||||
pub mod textfb;
|
||||
@@ -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 {}
|
||||
@@ -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
|
||||
@@ -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))]
|
||||
|
||||
@@ -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,5 +1,4 @@
|
||||
pub mod i8253;
|
||||
pub mod i8259;
|
||||
pub mod ps2;
|
||||
pub mod rtc;
|
||||
pub mod serial;
|
||||
|
||||
@@ -56,10 +56,6 @@ pub const fn arch_str() -> &'static str {
|
||||
{
|
||||
"riscv64"
|
||||
}
|
||||
#[cfg(target_arch = "x86")]
|
||||
{
|
||||
"i686"
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a generic machine description string
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user