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