i686: add platform support

This commit is contained in:
Mark Poliakov 2024-10-10 18:06:54 +03:00
parent 7c38b84c39
commit 4b25a4db89
94 changed files with 3979 additions and 545 deletions

16
Cargo.lock generated
View File

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

View 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
View 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 = .);
};

View File

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

View File

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

View 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 = "*"

View 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

View 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));

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

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

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

View 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()))
}
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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)
);

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

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

View File

@ -0,0 +1 @@
pub mod textfb;

View 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,
}
}

View 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

View File

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

View File

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

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

View File

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

View 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));

View 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

View File

@ -1,5 +1,4 @@
//! x86-64 platform peripheral drivers
pub mod i8253;
pub mod i8259;
pub mod ps2;
pub mod serial;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -37,6 +37,7 @@
#![no_std]
#![no_main]
use alloc::boxed::Box;
use arch::Platform;
use kernel_arch::{Architecture, ArchitectureImpl};
use libk::arch::Cpu;

View File

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

View File

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

View File

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

View File

@ -50,4 +50,8 @@ pub const fn arch_str() -> &'static str {
{
"x86_64"
}
#[cfg(target_arch = "x86")]
{
"i686"
}
}

View File

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

View File

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

View File

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

View File

@ -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
View 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";
}

View File

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

View File

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

View File

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

View 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
}};
}

View File

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

View File

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

View File

@ -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!(),

View File

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

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

View File

@ -12,7 +12,7 @@ members = [
"lib/libterm",
"netutils",
"netutils",
"dyn-loader",
"rdb"
# "dyn-loader",
# "rdb"
]
exclude = ["dynload-program", "test-kernel-module"]

View File

@ -0,0 +1,4 @@
init:1:wait:/sbin/rc default
logd:1:once:/sbin/logd
user:1:once:/sbin/login /dev/tty0

View File

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

View File

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

View File

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

View File

@ -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"))?;

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#![deny(warnings)]
// #![deny(warnings)]
use std::{fs, path::PathBuf, process::ExitCode};

View File

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