kernel: fix some TODOs

This commit is contained in:
Mark Poliakov 2024-07-27 14:37:46 +03:00
parent a3d7ecd867
commit b9c1b15bd1
27 changed files with 188 additions and 478 deletions

View File

@ -213,6 +213,11 @@ impl<L: EntryLevel> PageTable<L> {
Ok(table) Ok(table)
} }
pub unsafe fn free<TA: TableAllocator>(this: PhysicalRefMut<Self, KernelTableManagerImpl>) {
let physical = this.as_physical_address();
TA::free_page_table(physical);
}
// /// Returns the physical address of this table // /// Returns the physical address of this table
// pub fn physical_address(&self) -> usize { // pub fn physical_address(&self) -> usize {
// unsafe { (self.data.as_ptr() as usize).physicalize() } // unsafe { (self.data.as_ptr() as usize).physicalize() }

View File

@ -106,6 +106,7 @@ __aarch64_ap_entry:
isb isb
mov sp, x0 mov sp, x0
bl {kernel_ap_lower_entry} - {kernel_virt_offset} MOV_ABS x0, {kernel_ap_lower_entry} - {kernel_virt_offset}
blr x0
b . b .

View File

@ -16,8 +16,12 @@ use libk_mm::{
}; };
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use super::{exception, BootStack, BOOT_STACK_SIZE, PLATFORM}; use super::{exception, BootStack, PLATFORM};
use crate::{arch::L3, kernel_main, kernel_secondary_main, mem::KERNEL_VIRT_OFFSET}; use crate::{
arch::{aarch64::BOOT_STACK_SIZE, L3},
kernel_main, kernel_secondary_main,
mem::KERNEL_VIRT_OFFSET,
};
unsafe fn pre_init_mmu() { unsafe fn pre_init_mmu() {
if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran4::Supported) { if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran4::Supported) {
@ -51,7 +55,7 @@ unsafe fn enable_mmu() {
SCTLR_EL1.modify( SCTLR_EL1.modify(
// Enable translation // Enable translation
SCTLR_EL1::M::Enable + SCTLR_EL1::M::Enable +
// (TODO) Disable I + D caches // NOTE if anything breaks due to caching, find out :)
SCTLR_EL1::I::NonCacheable + SCTLR_EL1::C::NonCacheable, SCTLR_EL1::I::NonCacheable + SCTLR_EL1::C::NonCacheable,
); );
@ -81,11 +85,10 @@ unsafe extern "C" fn __aarch64_el1_bsp_lower_entry(dtb: PhysicalAddress) -> ! {
kernel_arch_aarch64::mem::load_fixed_tables(); kernel_arch_aarch64::mem::load_fixed_tables();
enable_mmu(); enable_mmu();
// Safety: SP points to the .bss section, so it's +offset mapped // SP points to the .bss section, so it's +offset mapped
let sp = unsafe { BSP_STACK.data.as_ptr().add(BOOT_STACK_SIZE) as usize } + KERNEL_VIRT_OFFSET; let sp = BSP_STACK.top_addr() + KERNEL_VIRT_OFFSET;
let elr = absolute_address!(__aarch64_bsp_upper_entry); let elr = absolute_address!(__aarch64_bsp_upper_entry);
// TODO pass dtb
enter_higher_half(sp, elr, dtb.into_usize()); enter_higher_half(sp, elr, dtb.into_usize());
} }
@ -154,9 +157,7 @@ extern "C" fn __aarch64_ap_upper_entry() -> ! {
} }
#[link_section = ".bss"] #[link_section = ".bss"]
static BSP_STACK: BootStack = BootStack { static BSP_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
data: [0; BOOT_STACK_SIZE],
};
global_asm!( global_asm!(
include_str!("entry.S"), include_str!("entry.S"),

View File

@ -9,16 +9,16 @@ use aarch64_cpu::{
VBAR_EL1, VBAR_EL1,
}, },
}; };
use abi::{ use abi::{process::Signal, SyscallFunction};
process::{Signal, SignalEntryData}, use kernel_arch::{Architecture, ArchitectureImpl};
SyscallFunction,
};
use kernel_arch::{task::TaskFrame, Architecture, ArchitectureImpl};
use kernel_arch_aarch64::context::ExceptionFrame; use kernel_arch_aarch64::context::ExceptionFrame;
use libk::{device::external_interrupt_controller, task::thread::Thread}; use libk::{arch::Cpu, device::external_interrupt_controller, task::thread::Thread};
use tock_registers::interfaces::{Readable, Writeable}; use tock_registers::interfaces::{Readable, Writeable};
use crate::{debug::LogLevel, syscall::raw_syscall_handler}; use crate::{
debug::LogLevel,
syscall::{self, raw_syscall_handler},
};
/// Initializes the exception/interrupt vectors. May be called repeatedly (though that makes no /// Initializes the exception/interrupt vectors. May be called repeatedly (though that makes no
/// sense). /// sense).
@ -32,7 +32,7 @@ pub fn init_exceptions() {
} }
fn dump_irrecoverable_exception(frame: &ExceptionFrame, ec: u64, iss: u64) { fn dump_irrecoverable_exception(frame: &ExceptionFrame, ec: u64, iss: u64) {
// let cpu = Cpu::get_local(); let cpu = Cpu::try_local();
log_print_raw!(LogLevel::Fatal, "SYNC exception:\n"); log_print_raw!(LogLevel::Fatal, "SYNC exception:\n");
log_print_raw!(LogLevel::Fatal, "FAR: {:#x}\n", FAR_EL1.get()); log_print_raw!(LogLevel::Fatal, "FAR: {:#x}\n", FAR_EL1.get());
@ -43,14 +43,14 @@ fn dump_irrecoverable_exception(frame: &ExceptionFrame, ec: u64, iss: u64) {
log_print_raw!(LogLevel::Fatal, "Register dump:\n"); log_print_raw!(LogLevel::Fatal, "Register dump:\n");
log_print_raw!(LogLevel::Fatal, "{:?}\n", frame); log_print_raw!(LogLevel::Fatal, "{:?}\n", frame);
// XXX if let Some(cpu) = cpu {
// if let Some(cpu) = cpu { // let current = cpu.queue().current_process();
// let current = cpu.queue().current_process(); let current = cpu.current_thread_id().and_then(Thread::get);
// if let Some(current) = current { if let Some(current) = current {
// log_print_raw!(LogLevel::Fatal, "In process {}\n", current.id()); log_print_raw!(LogLevel::Fatal, "In thread {}\n", current.id);
// } }
// } }
match ec { match ec {
// Data abort from lower level // Data abort from lower level
@ -177,7 +177,7 @@ fn el0_sync_inner(frame: &mut ExceptionFrame) {
let func = frame.r[8]; let func = frame.r[8];
if func == usize::from(SyscallFunction::ExitSignal) as u64 { if func == usize::from(SyscallFunction::ExitSignal) as u64 {
unsafe { unsafe {
handle_signal_exit(frame); syscall::handle_signal_exit(frame);
} }
return; return;
} }
@ -226,16 +226,20 @@ fn irq_common() {
external_interrupt_controller().handle_pending_irqs(); external_interrupt_controller().handle_pending_irqs();
} }
unsafe fn handle_signal_exit(frame: &mut ExceptionFrame) { // unsafe fn handle_signal_exit(frame: &mut ExceptionFrame) {
// TODO validate the argument // let saved_data: &SignalEntryData = match syscall::arg::ref_const(frame.r[0] as _) {
let saved_data = &*(frame.r[0] as *const SignalEntryData); // Ok(r) => r,
debugln!( // Err(err) => {
"Handling signal exit to pc={:#x}, sp={:#x}", // todo!("Invalid SignalEntryData pointer: {:?}", err)
saved_data.frame.elr_el1, // }
saved_data.frame.sp_el0 // };
); // debugln!(
// "Handling signal exit to pc={:#x}, sp={:#x}",
frame.restore(&saved_data.frame); // saved_data.frame.elr_el1,
} // saved_data.frame.sp_el0
// );
//
// frame.restore(&saved_data.frame);
// }
global_asm!(include_str!("vectors.S")); global_asm!(include_str!("vectors.S"));

View File

@ -99,11 +99,11 @@ impl Gicd {
assert_eq!(interrupt_id & !0xF, 0); assert_eq!(interrupt_id & !0xF, 0);
let value = match target { let value = match target {
IpiDeliveryTarget::OtherCpus => SGIR::TargetListFilter::AllExceptLocal, IpiDeliveryTarget::OtherCpus => SGIR::TargetListFilter::AllExceptLocal,
IpiDeliveryTarget::Specific(_mask) => { IpiDeliveryTarget::Specific(mask) => {
// TODO: need to handle self-ipi case, releasing the lock somehow assert_eq!(mask & !0xFFFF, 0);
todo!(); SGIR::TargetListFilter::SpecifiedOnly + SGIR::CPUTargetList.val(mask as _)
} }
IpiDeliveryTarget::ThisCpu => todo!(), IpiDeliveryTarget::ThisCpu => SGIR::TargetListFilter::LocalOnly,
} + SGIR::INTID.val(interrupt_id as u32); } + SGIR::INTID.val(interrupt_id as u32);
self.shared_regs.lock().SGIR.write(value); self.shared_regs.lock().SGIR.write(value);
@ -114,7 +114,6 @@ impl Gicd {
} }
pub fn configure_irq(&self, irq: usize, options: IrqOptions) { pub fn configure_irq(&self, irq: usize, options: IrqOptions) {
// TODO configure trigger level
// 2 bits per IRQ, 16 entries per register // 2 bits per IRQ, 16 entries per register
let reg = irq / 16; let reg = irq / 16;
let shift = (irq % 16) * 2; let shift = (irq % 16) * 2;

View File

@ -163,18 +163,17 @@ impl MessageInterruptController for Gic {
impl LocalInterruptController for Gic { impl LocalInterruptController for Gic {
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error> { fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error> {
// TODO message queue insertion should be moved let local = cpu_index();
match target { let mask = match target {
IpiDeliveryTarget::OtherCpus => { IpiDeliveryTarget::OtherCpus => usize::MAX & !(1 << local),
let local = cpu_index(); IpiDeliveryTarget::Specific(mask) => mask,
for i in 0..CPU_COUNT.load(Ordering::Acquire) {
if i != local as usize {
Cpu::push_ipi_queue(i as u32, msg);
}
}
}
IpiDeliveryTarget::Specific(_) => todo!(),
IpiDeliveryTarget::ThisCpu => todo!(), IpiDeliveryTarget::ThisCpu => todo!(),
};
for i in 0..CPU_COUNT.load(Ordering::Acquire) {
if mask & (1 << i) != 0 {
Cpu::push_ipi_queue(i as _, msg);
}
} }
// Issue a memory barrier // Issue a memory barrier

View File

@ -49,8 +49,8 @@ const BOOT_STACK_SIZE: usize = 4096 * 32;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(C, align(0x20))] #[repr(C, align(0x20))]
struct BootStack { struct BootStack<const SIZE: usize> {
data: [u8; BOOT_STACK_SIZE], data: [u8; SIZE],
} }
/// AArch64 architecture implementation /// AArch64 architecture implementation
@ -64,6 +64,16 @@ pub struct AArch64 {
initrd: OneTimeInit<PhysicalRef<'static, [u8]>>, initrd: OneTimeInit<PhysicalRef<'static, [u8]>>,
} }
impl<const SIZE: usize> BootStack<SIZE> {
pub const fn zeroed() -> Self {
Self { data: [0; SIZE] }
}
pub fn top_addr(&self) -> usize {
unsafe { self.data.as_ptr().add(SIZE).addr() }
}
}
impl Platform for AArch64 { impl Platform for AArch64 {
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;

View File

@ -11,6 +11,9 @@ use crate::mem::KERNEL_VIRT_OFFSET;
use super::{BootStack, BOOT_STACK_SIZE}; use super::{BootStack, BOOT_STACK_SIZE};
#[link_section = ".bss"]
static AP_TRAMPOLINE_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
#[derive(Debug)] #[derive(Debug)]
enum CpuEnableMethod { enum CpuEnableMethod {
Psci, Psci,
@ -66,12 +69,6 @@ impl CpuEnableMethod {
} }
} }
// TODO can be made smaller
#[link_section = ".bss"]
static AP_TRAMPOLINE_STACK: BootStack = BootStack {
data: [0; BOOT_STACK_SIZE],
};
/// Starts application processors using the method specified in the device tree. /// Starts application processors using the method specified in the device tree.
/// ///
/// TODO: currently does not handle systems where APs are already started before entry. /// TODO: currently does not handle systems where APs are already started before entry.
@ -86,7 +83,7 @@ pub unsafe fn start_ap_cores(dt: &DeviceTree) -> Result<(), Error> {
} }
// Safety: safe, the stack is inside the kernel // Safety: safe, the stack is inside the kernel
let sp = AP_TRAMPOLINE_STACK.data.as_ptr() as usize - KERNEL_VIRT_OFFSET + BOOT_STACK_SIZE; let sp = AP_TRAMPOLINE_STACK.top_addr() - KERNEL_VIRT_OFFSET;
for cpu in enumerate_cpus(dt).filter(|cpu| cpu.id != 0) { for cpu in enumerate_cpus(dt).filter(|cpu| cpu.id != 0) {
debugln!( debugln!(

View File

@ -8,7 +8,7 @@
.macro EXC_HANDLER el, ht, bits, kind .macro EXC_HANDLER el, ht, bits, kind
__aa\bits\()_el\el\ht\()_\kind: __aa\bits\()_el\el\ht\()_\kind:
.if \bits == 32 .if \bits == 32
// TODO // TODO 32-bit support
b . b .
.endif .endif
@ -49,7 +49,6 @@ __aa\bits\()_el\el\ht\()_\kind:
mrs x2, sp_el0 mrs x2, sp_el0
mrs x3, mdscr_el1 mrs x3, mdscr_el1
// TODO
stp x0, x1, [sp, #16 * 16] stp x0, x1, [sp, #16 * 16]
stp x2, x3, [sp, #16 * 17] stp x2, x3, [sp, #16 * 17]
.endm .endm

View File

@ -271,11 +271,6 @@ impl IoApic {
}); });
} }
// TODO properly map this using DeviceMemory
// let regs = Regs {
// base: unsafe { PhysicalAddress::from_raw(ioapic.address as u64).virtualize_raw() },
// };
// let mapping = unsafe { DeviceMemoryMapping::map(base, size) };
let regs = unsafe { let regs = unsafe {
DeviceMemoryIo::<'_, Regs>::map( DeviceMemoryIo::<'_, Regs>::map(
PhysicalAddress::from_u64(ioapic.address as u64), PhysicalAddress::from_u64(ioapic.address as u64),

View File

@ -58,7 +58,6 @@ static YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 {
}; };
unsafe fn init_dummy_cpu() { unsafe fn init_dummy_cpu() {
// TODO this is incorrect
static UNINIT_CPU_INNER: usize = 0; static UNINIT_CPU_INNER: usize = 0;
static UNINIT_CPU_PTR: &usize = &UNINIT_CPU_INNER; static UNINIT_CPU_PTR: &usize = &UNINIT_CPU_INNER;

View File

@ -77,53 +77,6 @@ struct Pointer {
offset: usize, offset: usize,
} }
// impl ExceptionFrame {
// fn dump(&self, level: debug::LogLevel) {
// log_print_raw!(level, " CS:RIP = {:#x}:{:#x}\n", self.cs, self.rip);
// log_print_raw!(level, " SS:RSP = {:#x}:{:#x}\n", self.ss, self.rsp);
//
// log_print_raw!(
// level,
// "RAX = {:#018x}, RCX = {:#018x}\n",
// self.rax,
// self.rcx
// );
// log_print_raw!(
// level,
// "RDX = {:#018x}, RBX = {:#018x}\n",
// self.rdx,
// self.rbx
// );
// log_print_raw!(
// level,
// "RSI = {:#018x}, RDI = {:#018x}\n",
// self.rsi,
// self.rdi
// );
// log_print_raw!(level, "RBP = {:#018x}\n\n", self.rbp);
//
// log_print_raw!(level, " R8 = {:#018x}, R9 = {:#018x}\n", self.r8, self.r9);
// log_print_raw!(
// level,
// "R10 = {:#018x}, R11 = {:#018x}\n",
// self.r10,
// self.r11
// );
// log_print_raw!(
// level,
// "R12 = {:#018x}, R13 = {:#018x}\n",
// self.r12,
// self.r13
// );
// log_print_raw!(
// level,
// "R14 = {:#018x}, R15 = {:#018x}\n",
// self.r14,
// self.r15
// );
// }
// }
const SIZE: usize = 256; const SIZE: usize = 256;
impl Entry { impl Entry {

View File

@ -194,8 +194,6 @@ impl X86_64 {
for l1i in 0..end_l1i { for l1i in 0..end_l1i {
let l2_phys_addr = l2_tables_start.add(l1i * L3::SIZE); let l2_phys_addr = l2_tables_start.add(l1i * L3::SIZE);
// TODO (minor) the slice is uninitialized, maybe find some way to deal with that
// case nicely
// Safety: ok, the mapping is done to the memory obtained from // Safety: ok, the mapping is done to the memory obtained from
// find_contiguous_region() // find_contiguous_region()
let mut l2_data = let mut l2_data =

View File

@ -112,5 +112,6 @@ pub unsafe fn start_ap_cores(info: &ProcessorInfo<AcpiAllocator>) {
flush_tlb_entry(0); flush_tlb_entry(0);
KERNEL_TABLES.l0.data[0] = 0; KERNEL_TABLES.l0.data[0] = 0;
// TODO drop the tables PageTable::free::<TableAllocatorImpl>(identity_l1);
PageTable::free::<TableAllocatorImpl>(identity_l2);
} }

View File

@ -22,6 +22,9 @@
.set REG_R14, 16 * 8 // .set REG_R14, 16 * 8 //
.set REG_R15, 17 * 8 // .set REG_R15, 17 * 8 //
.set GS_TSS, 0x8
.set GS_TMP, 0x10
// 15 general-purpose registers // 15 general-purpose registers
// user ip // user ip
// user sp // user sp
@ -49,7 +52,7 @@
movq %rcx, REG_RCX(%rsp) movq %rcx, REG_RCX(%rsp)
movq %r11, REG_R11(%rsp) movq %r11, REG_R11(%rsp)
movq %gs:(16), %rax movq %gs:(GS_TMP), %rax
movq %rcx, REG_USER_IP(%rsp) movq %rcx, REG_USER_IP(%rsp)
movq %rax, REG_USER_SP(%rsp) movq %rax, REG_USER_SP(%rsp)
movq %r11, REG_USER_FLAGS(%rsp) movq %r11, REG_USER_FLAGS(%rsp)
@ -75,10 +78,9 @@ __x86_64_syscall_vector:
swapgs swapgs
// Store user RSP // Store user RSP
// TODO: eliminate magic %gs-relative addresses mov %rsp, %gs:(GS_TMP)
mov %rsp, %gs:(16)
// Load the task's RSP0 from TSS // Load the task's RSP0 from TSS
mov %gs:(8), %rsp mov %gs:(GS_TSS), %rsp
mov 4(%rsp), %rsp mov 4(%rsp), %rsp
SYSCALL_SAVE_STATE SYSCALL_SAVE_STATE
@ -133,7 +135,7 @@ __x86_64_syscall_vector:
.return_via_sysret: .return_via_sysret:
// Regular syscall return // Regular syscall return
movq REG_USER_SP(%rsp), %rcx movq REG_USER_SP(%rsp), %rcx
movq %rcx, %gs:(16) movq %rcx, %gs:(GS_TMP)
movq REG_USER_IP(%rsp), %rcx movq REG_USER_IP(%rsp), %rcx
movq REG_USER_FLAGS(%rsp), %r11 movq REG_USER_FLAGS(%rsp), %r11
@ -142,7 +144,7 @@ __x86_64_syscall_vector:
// %rcx and %r11 now contain the expected values // %rcx and %r11 now contain the expected values
// Restore user RSP // Restore user RSP
mov %gs:(16), %rsp mov %gs:(GS_TMP), %rsp
swapgs swapgs
sysretq sysretq

View File

@ -2,8 +2,7 @@
use core::arch::global_asm; use core::arch::global_asm;
use abi::{process::SignalEntryData, SyscallFunction}; use abi::SyscallFunction;
use kernel_arch::task::TaskFrame;
use kernel_arch_x86_64::{ use kernel_arch_x86_64::{
context::SyscallFrame, context::SyscallFrame,
registers::{MSR_IA32_EFER, MSR_IA32_LSTAR, MSR_IA32_SFMASK, MSR_IA32_STAR}, registers::{MSR_IA32_EFER, MSR_IA32_LSTAR, MSR_IA32_SFMASK, MSR_IA32_STAR},
@ -11,12 +10,12 @@ use kernel_arch_x86_64::{
use libk::task::{process::Process, thread::Thread}; use libk::task::{process::Process, thread::Thread};
use tock_registers::interfaces::{ReadWriteable, Writeable}; use tock_registers::interfaces::{ReadWriteable, Writeable};
use crate::syscall::raw_syscall_handler; use crate::syscall::{self, raw_syscall_handler};
fn syscall_inner(frame: &mut SyscallFrame) { fn syscall_inner(frame: &mut SyscallFrame) {
if frame.rax == usize::from(SyscallFunction::ExitSignal) as u64 { if frame.rax == usize::from(SyscallFunction::ExitSignal) as u64 {
unsafe { unsafe {
handle_signal_exit(frame); syscall::handle_signal_exit(frame);
return; return;
} }
} }
@ -42,18 +41,6 @@ extern "C" fn __x86_64_syscall_handler(frame: *mut SyscallFrame) {
} }
} }
unsafe fn handle_signal_exit<F: TaskFrame>(frame: &mut F) {
// TODO validate the argument
let saved_data = &*(frame.argument() as *const SignalEntryData);
infoln!(
"Handling signal exit to ip={:#x}, sp={:#x}",
saved_data.frame.user_ip,
saved_data.frame.user_sp
);
frame.restore(&saved_data.frame);
}
/// Initializes system call instruction support for the current CPU. /// Initializes system call instruction support for the current CPU.
/// ///
/// # Safety /// # Safety

View File

@ -1,108 +0,0 @@
//! Allwinner (H6) R Watchdog driver
use abi::error::Error;
use alloc::boxed::Box;
use device_api::{Device, ResetDevice};
use libk::util::OneTimeInit;
use tock_registers::{
interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite,
};
use crate::{
arch::{Architecture, PLATFORM},
device::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt},
device_tree_driver,
mem::device::DeviceMemoryIo,
sync::IrqSafeSpinlock,
};
register_bitfields! {
u32,
CTRL [
KEY OFFSET(1) NUMBITS(12) [
Value = 0xA57
],
RESTART OFFSET(0) NUMBITS(1) []
],
CFG [
CONFIG OFFSET(0) NUMBITS(2) [
System = 1,
]
],
MODE [
EN OFFSET(0) NUMBITS(1) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => IRQ_EN: ReadWrite<u32>),
(0x04 => IRQ_STA: ReadWrite<u32>),
(0x08 => _0),
(0x10 => CTRL: ReadWrite<u32, CTRL::Register>),
(0x14 => CFG: ReadWrite<u32, CFG::Register>),
(0x18 => MODE: ReadWrite<u32, MODE::Register>),
(0x1C => @END),
}
}
struct Inner {
regs: DeviceMemoryIo<Regs>,
}
struct RWdog {
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
base: usize,
}
impl ResetDevice for RWdog {
unsafe fn reset(&self) -> ! {
// TODO disable IRQs
let inner = self.inner.get().lock();
inner.regs.CFG.write(CFG::CONFIG::System);
inner.regs.MODE.write(MODE::EN::SET);
inner.regs.CTRL.write(CTRL::KEY::Value + CTRL::RESTART::SET);
loop {
core::arch::asm!("wfe");
}
}
}
impl Device for RWdog {
fn display_name(&self) -> &'static str {
"Allwinner H6 Watchdog"
}
unsafe fn init(&'static self) -> Result<(), Error> {
let regs = DeviceMemoryIo::map("r_wdog", self.base)?;
self.inner.init(IrqSafeSpinlock::new(Inner { regs }));
PLATFORM.register_reset_device(self)?;
Ok(())
}
}
device_tree_driver! {
compatible: ["allwinner,sun50i-h6-wdt"],
probe(of) => {
let reg = devtree::find_prop(&of.node, "reg")?;
let status: &str = of.node.prop("status").unwrap_or("enabled");
if status == "disabled" {
return None;
}
let (base, _) = reg.cell2_array_item(0, of.address_cells, of.size_cells)?;
let base = base as usize;
Some(Box::new(RWdog {
inner: OneTimeInit::new(),
base
}))
}
}

View File

@ -1,206 +0,0 @@
//! Allwinner (H6) UART implementation
use abi::{error::Error, io::DeviceRequest};
use alloc::boxed::Box;
use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device};
use libk::util::OneTimeInit;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use vfs::CharDevice;
use crate::{
arch::{aarch64::IrqNumber, Architecture, PLATFORM},
debug::{self, DebugSink, LogLevel},
device::{
devtree::{self, DevTreeIndexPropExt},
tty::{CharRing, TtyDevice},
},
device_tree_driver,
fs::devfs::{self, CharDeviceType},
mem::device::DeviceMemoryIo,
sync::IrqSafeSpinlock,
};
register_bitfields! {
u32,
USR [
TFE OFFSET(2) NUMBITS(1) [],
TFNF OFFSET(1) NUMBITS(1) []
],
IER [
ERBFI OFFSET(0) NUMBITS(1) [],
],
IIR [
IID OFFSET(0) NUMBITS(4) [
RecvDataAvailable = 0b0100,
]
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => DLL: ReadWrite<u32>),
(0x04 => IER: ReadWrite<u32, IER::Register>),
(0x08 => IIR: ReadWrite<u32, IIR::Register>),
(0x0C => _0),
(0x7C => USR: ReadOnly<u32, USR::Register>),
(0x80 => @END),
}
}
struct Inner {
regs: DeviceMemoryIo<Regs>,
}
struct SunxiUart {
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
base: usize,
irq: IrqNumber,
ring: CharRing<16>,
}
impl DebugSink for SunxiUart {
fn putc(&self, c: u8) -> Result<(), Error> {
self.send(c)
}
fn supports_control_sequences(&self) -> bool {
true
}
}
impl CharDevice for SunxiUart {
fn read(&'static self, _blocking: bool, data: &mut [u8]) -> Result<usize, Error> {
self.line_read(data)
}
fn write(&self, _blocking: bool, data: &[u8]) -> Result<usize, Error> {
self.line_write(data)
}
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
match req {
&mut DeviceRequest::SetTerminalGroup(id) => {
self.set_signal_group(id as _);
Ok(())
}
_ => Err(Error::InvalidArgument),
}
}
}
impl TtyDevice<16> for SunxiUart {
fn ring(&self) -> &CharRing<16> {
&self.ring
}
}
impl InterruptHandler for SunxiUart {
fn handle_irq(&self) -> bool {
let inner = self.inner.get().lock();
if inner.regs.IIR.matches_all(IIR::IID::RecvDataAvailable) {
let byte = inner.regs.DLL.get();
drop(inner);
if byte == b'\x1b' as u32 {
panic!("RESET TRIGGERED");
}
self.recv_byte(byte as u8);
}
// inner.regs.ICR.write(ICR::ALL::CLEAR);
// let byte = inner.regs.DR.get();
// drop(inner);
// if byte == b'\x1b' as u32 {
// use crate::task::sched::CpuQueue;
// for (i, queue) in CpuQueue::all().enumerate() {
// log_print_raw!(LogLevel::Fatal, "queue{}:\n", i);
// let lock = unsafe { queue.grab() };
// for item in lock.iter() {
// log_print_raw!(
// LogLevel::Fatal,
// "* {} {:?} {:?}\n",
// item.id(),
// item.name(),
// item.state()
// );
// }
// }
// } else {
// self.recv_byte(byte as u8);
// }
true
}
}
impl SerialDevice for SunxiUart {
fn send(&self, byte: u8) -> Result<(), Error> {
let inner = self.inner.get().lock();
if byte == b'\n' {
while inner.regs.USR.matches_all(USR::TFE::CLEAR) {
core::hint::spin_loop();
}
inner.regs.DLL.set(b'\r' as u32);
}
while inner.regs.USR.matches_all(USR::TFE::CLEAR) {
core::hint::spin_loop();
}
inner.regs.DLL.set(byte as u32);
Ok(())
}
}
impl Device for SunxiUart {
fn display_name(&self) -> &'static str {
"Allwinner UART"
}
unsafe fn init(&'static self) -> Result<(), Error> {
let regs = DeviceMemoryIo::<Regs>::map("sunxi-uart", self.base)?;
self.inner.init(IrqSafeSpinlock::new(Inner { regs }));
debug::add_sink(self, LogLevel::Debug);
devfs::add_char_device(self, CharDeviceType::TtySerial)?;
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = PLATFORM.external_interrupt_controller();
intc.register_irq(self.irq, Default::default(), self)?;
intc.enable_irq(self.irq)?;
let inner = self.inner.get().lock();
inner.regs.IER.modify(IER::ERBFI::SET);
Ok(())
}
}
device_tree_driver! {
compatible: ["snps,dw-apb-uart"],
probe(of) => {
let reg = devtree::find_prop(&of.node, "reg")?;
let (base, _) = reg.cell2_array_item(0, of.address_cells, of.size_cells)?;
if base == 0x05000000 {
Some(Box::new(SunxiUart {
inner: OneTimeInit::new(),
ring: CharRing::new(),
irq: IrqNumber::Shared(0),
base: base as usize
}))
} else {
// TODO don't just hardcode and ignore other UARTs
None
}
}
}

View File

@ -13,6 +13,7 @@ use libk_mm::{
}; };
use libk_util::OneTimeInit; use libk_util::OneTimeInit;
use memfs::block::{self, BlockAllocator}; use memfs::block::{self, BlockAllocator};
use static_assertions::const_assert_eq;
use yggdrasil_abi::{error::Error, io::MountOptions}; use yggdrasil_abi::{error::Error, io::MountOptions};
// pub mod devfs; // pub mod devfs;
@ -34,10 +35,10 @@ pub static INITRD_DATA: OneTimeInit<Initrd> = OneTimeInit::new();
/// Implementation of [memfs::block::BlockAllocator] for the kernel /// Implementation of [memfs::block::BlockAllocator] for the kernel
pub struct FileBlockAllocator; pub struct FileBlockAllocator;
const_assert_eq!(block::SIZE, 4096);
unsafe impl BlockAllocator for FileBlockAllocator { unsafe impl BlockAllocator for FileBlockAllocator {
fn alloc() -> Result<NonNull<u8>, Error> { fn alloc() -> Result<NonNull<u8>, Error> {
// TODO make this a static assertion
assert_eq!(block::SIZE, 4096);
let page = phys::alloc_page()?; let page = phys::alloc_page()?;
Ok(unsafe { NonNull::new_unchecked(page.virtualize() as *mut _) }) Ok(unsafe { NonNull::new_unchecked(page.virtualize() as *mut _) })
} }

View File

@ -68,7 +68,8 @@ pub fn kinit() -> Result<(), Error> {
let mut ioctx = IoContext::new(root); let mut ioctx = IoContext::new(root);
// TODO maybe better to load this from userspace or along with the kernel // 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")?;
{ {

View File

@ -1,18 +1,22 @@
//! System call argument validation functions
use libk::task::{mem::ForeignPointer, thread::Thread}; use libk::task::{mem::ForeignPointer, thread::Thread};
use yggdrasil_abi::error::Error; use yggdrasil_abi::error::Error;
pub(super) fn ref_const<'a, T: Sized>(addr: usize) -> Result<&'a T, Error> { /// Validates a &T passed as an address
pub fn ref_const<'a, T: Sized>(addr: usize) -> Result<&'a T, Error> {
let proc = Thread::current(); let proc = Thread::current();
let ptr = addr as *const T; let ptr = addr as *const T;
unsafe { ptr.validate_user_ptr(proc.address_space()) } unsafe { ptr.validate_user_ptr(proc.address_space()) }
} }
pub(super) fn ref_mut<'a, T: Sized>(addr: usize) -> Result<&'a mut T, Error> { /// Validates a &mut T passed as an address
pub fn ref_mut<'a, T: Sized>(addr: usize) -> Result<&'a mut T, Error> {
let proc = Thread::current(); let proc = Thread::current();
let ptr = addr as *mut T; let ptr = addr as *mut T;
unsafe { ptr.validate_user_mut(proc.address_space()) } unsafe { ptr.validate_user_mut(proc.address_space()) }
} }
pub(super) fn str_ref<'a>(base: usize, len: usize) -> Result<&'a str, Error> { /// Validates a &str passed as base address and length
pub fn str_ref<'a>(base: usize, len: usize) -> Result<&'a str, Error> {
let slice = slice_ref(base, len)?; let slice = slice_ref(base, len)?;
if slice.contains(&0) { if slice.contains(&0) {
warnln!("User-supplied string contains NUL characters"); warnln!("User-supplied string contains NUL characters");
@ -21,12 +25,14 @@ pub(super) fn str_ref<'a>(base: usize, len: usize) -> Result<&'a str, Error> {
Ok(core::str::from_utf8(slice).unwrap()) Ok(core::str::from_utf8(slice).unwrap())
} }
pub(super) fn slice_ref<'a, T: Sized>(base: usize, count: usize) -> Result<&'a [T], Error> { /// Validates a &[T] passed as base address and element count
pub fn slice_ref<'a, T: Sized>(base: usize, count: usize) -> Result<&'a [T], Error> {
let proc = Thread::current(); let proc = Thread::current();
let ptr = base as *const T; let ptr = base as *const T;
unsafe { ptr.validate_user_slice(count, proc.address_space()) } unsafe { ptr.validate_user_slice(count, proc.address_space()) }
} }
pub(super) fn slice_mut<'a, T: Sized>(base: usize, count: usize) -> Result<&'a mut [T], Error> { /// Validates a &mut [T] passed as base address and element count
pub fn slice_mut<'a, T: Sized>(base: usize, count: usize) -> Result<&'a mut [T], Error> {
let proc = Thread::current(); let proc = Thread::current();
let ptr = base as *mut T; let ptr = base as *mut T;
unsafe { ptr.validate_user_slice_mut(count, proc.address_space()) } unsafe { ptr.validate_user_slice_mut(count, proc.address_space()) }

View File

@ -20,7 +20,7 @@ use libk_mm::{
table::{EntryLevelExt, MapAttributes}, table::{EntryLevelExt, MapAttributes},
}; };
use crate::{arch::L3, proc, syscall::run_with_io}; use crate::{arch::L3, proc, syscall::run_with_io, util::IteratorExt};
// Memory management // Memory management
pub(crate) fn map_memory( pub(crate) fn map_memory(
@ -89,16 +89,20 @@ pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Err
let process = thread.process(); let process = thread.process();
run_with_io(&process, |mut io| { run_with_io(&process, |mut io| {
let mut attach_debugger = None; // let mut attach_debugger = None;
// TODO try_find_map() let attach_debugger = options
for entry in options.optional { .optional
if let &SpawnOption::AttachDebug(fd) = entry { .iter()
let channel = io.files.file(fd)?; .try_find_map::<_, Error, _>(|entry| {
let channel = channel.as_message_channel()?.clone(); if let &SpawnOption::AttachDebug(fd) = entry {
let channel = io.files.file(fd)?;
let channel = channel.as_message_channel()?.clone();
attach_debugger = Some(channel); Ok(Some(channel))
} } else {
} Ok(None)
}
})?;
// Setup a new process from the file // Setup a new process from the file
let (child_process, child_main) = proc::load_binary( let (child_process, child_main) = proc::load_binary(

View File

@ -1,13 +1,14 @@
//! System function call handlers //! System function call handlers
use abi::{error::Error, io::RawFd, SyscallFunction}; use abi::{error::Error, io::RawFd, process::SignalEntryData, SyscallFunction};
use kernel_arch::task::TaskFrame;
use libk::{ use libk::{
task::process::{Process, ProcessIo}, task::process::{Process, ProcessIo},
vfs::NodeRef, vfs::NodeRef,
}; };
use libk_util::sync::IrqSafeSpinlockGuard; use libk_util::sync::IrqSafeSpinlockGuard;
mod arg; pub mod arg;
mod imp; mod imp;
fn run_with_io<T, F: FnOnce(IrqSafeSpinlockGuard<ProcessIo>) -> T>(proc: &Process, f: F) -> T { fn run_with_io<T, F: FnOnce(IrqSafeSpinlockGuard<ProcessIo>) -> T>(proc: &Process, f: F) -> T {
@ -34,6 +35,23 @@ fn run_with_io_at<T, F: FnOnce(NodeRef, IrqSafeSpinlockGuard<ProcessIo>) -> Resu
f(at, io) f(at, io)
} }
/// Handles "return from signal" syscall
pub unsafe fn handle_signal_exit<F: TaskFrame>(frame: &mut F) {
let saved_data: &SignalEntryData = match arg::ref_const(frame.argument() as _) {
Ok(r) => r,
Err(err) => {
todo!("Invalid SignalEntryData pointer: {:?}", err)
}
};
// infoln!(
// "Handling signal exit to ip={:#x}, sp={:#x}",
// saved_data.frame.user_ip,
// saved_data.frame.user_sp
// );
frame.restore(&saved_data.frame);
}
mod generated { mod generated {
#![allow(unreachable_code)] #![allow(unreachable_code)]

View File

@ -6,6 +6,15 @@ pub trait ResultIterator<T, E> {
fn collect_error(self) -> Option<E>; fn collect_error(self) -> Option<E>;
} }
/// Extension trait for [Iterator]s with extra functionality
pub trait IteratorExt<T> {
/// Like find_map(), but with [Result] handling
fn try_find_map<U, E, F: Fn(T) -> Result<Option<U>, E>>(
self,
mapper: F,
) -> Result<Option<U>, E>;
}
impl<T, E, I: Iterator<Item = Result<T, E>>> ResultIterator<T, E> for I { impl<T, E, I: Iterator<Item = Result<T, E>>> ResultIterator<T, E> for I {
fn collect_error(self) -> Option<E> { fn collect_error(self) -> Option<E> {
for item in self { for item in self {
@ -17,6 +26,20 @@ impl<T, E, I: Iterator<Item = Result<T, E>>> ResultIterator<T, E> for I {
} }
} }
impl<T, I: Iterator<Item = T>> IteratorExt<T> for I {
fn try_find_map<U, E, F: Fn(T) -> Result<Option<U>, E>>(
self,
mapper: F,
) -> Result<Option<U>, E> {
for item in self {
if let Some(entry) = mapper(item)? {
return Ok(Some(entry));
}
}
Ok(None)
}
}
/// Returns the architecture name string /// Returns the architecture name string
pub const fn arch_str() -> &'static str { pub const fn arch_str() -> &'static str {
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]

View File

@ -39,7 +39,9 @@ pub trait NetValueImpl: Copy + Eq + bytemuck::Pod + bytemuck::Zeroable {
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))] #[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(transparent)] #[repr(transparent)]
pub struct NetValue<T: NetValueImpl>(T); pub struct NetValue<T>(T)
where
T: NetValueImpl;
/// Helper type for primitive values wrapped in some 1-struct /// Helper type for primitive values wrapped in some 1-struct
#[cfg(not(feature = "bytemuck"))] #[cfg(not(feature = "bytemuck"))]

View File

@ -114,6 +114,12 @@ impl<A: Architecture> Qemu<A> {
self self
} }
pub fn with_smp(&mut self, smp: usize) -> &mut Self {
assert_ne!(smp, 0);
self.smp = Some(smp);
self
}
pub fn disable_display(&mut self) -> &mut Self { pub fn disable_display(&mut self) -> &mut Self {
self.no_display = true; self.no_display = true;
self self

View File

@ -39,11 +39,12 @@ struct QemuAArch64MachineConfig {
memory: usize, memory: usize,
} }
#[derive(Debug, Default, serde::Deserialize)] #[derive(Debug, serde::Deserialize)]
#[serde(default)] #[serde(default)]
struct QemuMachineConfig { struct QemuMachineConfig {
x86_64: QemuX86_64MachineConfig, x86_64: QemuX86_64MachineConfig,
aarch64: QemuAArch64MachineConfig, aarch64: QemuAArch64MachineConfig,
smp: usize,
} }
#[derive(Debug, Default, serde::Deserialize)] #[derive(Debug, Default, serde::Deserialize)]
@ -53,6 +54,16 @@ struct QemuConfig {
machine: QemuMachineConfig, machine: QemuMachineConfig,
} }
impl Default for QemuMachineConfig {
fn default() -> Self {
Self {
x86_64: Default::default(),
aarch64: Default::default(),
smp: 4,
}
}
}
impl Default for QemuAArch64MachineConfig { impl Default for QemuAArch64MachineConfig {
fn default() -> Self { fn default() -> Self {
Self { memory: 512 } Self { memory: 512 }
@ -106,6 +117,7 @@ fn run_aarch64(
} }
qemu.with_serial(QemuSerialTarget::MonStdio) qemu.with_serial(QemuSerialTarget::MonStdio)
.with_cpu(aarch64::Cpu::CortexA57) .with_cpu(aarch64::Cpu::CortexA57)
.with_smp(config.machine.smp)
.with_machine(aarch64::Machine::Virt { virtualize: true }) .with_machine(aarch64::Machine::Virt { virtualize: true })
.with_boot_image(aarch64::Image::Kernel { .with_boot_image(aarch64::Image::Kernel {
kernel: kernel_bin, kernel: kernel_bin,
@ -135,6 +147,7 @@ fn run_x86_64(
.with_cpu(x86_64::Cpu::Host { .with_cpu(x86_64::Cpu::Host {
enable_kvm: config.machine.x86_64.enable_kvm, enable_kvm: config.machine.x86_64.enable_kvm,
}) })
.with_smp(config.machine.smp)
.with_machine(x86_64::Machine::Q35) .with_machine(x86_64::Machine::Q35)
.with_boot_slot('a') .with_boot_slot('a')
.with_bios_image(X86_64_UEFI_PATH.into()) .with_bios_image(X86_64_UEFI_PATH.into())