dev: use device tree instead of hardcoded board impls
This commit is contained in:
parent
37e6fc098b
commit
e694c1cef0
Cargo.toml
lib/device-api
src
arch
aarch64
mod.rsx86_64/boot
device
main.rsmem
panic.rsproc
task
@ -10,6 +10,7 @@ build = "build.rs"
|
||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
vfs = { path = "lib/vfs" }
|
||||
memfs = { path = "lib/memfs" }
|
||||
device-api = { path = "lib/device-api", features = ["derive"] }
|
||||
|
||||
atomic_enum = "0.2.0"
|
||||
bitflags = "2.3.3"
|
||||
@ -45,3 +46,4 @@ default = []
|
||||
fb_console = ["bitmap-font", "embedded-graphics"]
|
||||
aarch64_qemu = []
|
||||
aarch64_orange_pi3 = []
|
||||
aarch64_rpi3b = []
|
||||
|
14
lib/device-api/Cargo.toml
Normal file
14
lib/device-api/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "device-api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
macros = { path = "macros", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
derive = ["macros"]
|
14
lib/device-api/macros/Cargo.toml
Normal file
14
lib/device-api/macros/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.66"
|
||||
quote = "1.0.32"
|
||||
syn = { version = "2.0.28", features = ["full"] }
|
0
lib/device-api/macros/src/lib.rs
Normal file
0
lib/device-api/macros/src/lib.rs
Normal file
7
lib/device-api/src/bus.rs
Normal file
7
lib/device-api/src/bus.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::manager::DeviceManager;
|
||||
|
||||
pub trait Bus {
|
||||
fn enumerate(&self, manager: &mut DeviceManager) -> Result<(), Error>;
|
||||
}
|
37
lib/device-api/src/device.rs
Normal file
37
lib/device-api/src/device.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct DeviceId(u64);
|
||||
|
||||
pub trait Device: Send + 'static {
|
||||
fn display_name(&self) -> &'static str;
|
||||
|
||||
/// Initializes the device, making it ready for operation.
|
||||
/// The method is also responsible for registering the device with appropriate OS subsystems
|
||||
/// (e.g. registering a terminal ttySn for a serial port)
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must make sure the function is only called once.
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initializes the IRQ handling options on this device: binds its IRQ(s) to their handlers and
|
||||
/// enables their reception.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must make sure the function is only called once. The caller must also make sure
|
||||
/// the function is not called before the device's [Device::init] is called.
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for DeviceId {
|
||||
fn from(value: usize) -> Self {
|
||||
Self(value as u64)
|
||||
}
|
||||
}
|
116
lib/device-api/src/interrupt.rs
Normal file
116
lib/device-api/src/interrupt.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::Device;
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum IrqLevel {
|
||||
#[default]
|
||||
Default,
|
||||
ActiveHigh,
|
||||
ActiveLow,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum IrqTrigger {
|
||||
#[default]
|
||||
Default,
|
||||
Edge,
|
||||
Level,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IpiDeliveryTarget {
|
||||
Specific(usize),
|
||||
ThisCpu,
|
||||
OtherCpus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IrqNumber {
|
||||
Private(u32),
|
||||
Shared(u32),
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct IrqOptions {
|
||||
pub level: IrqLevel,
|
||||
pub trigger: IrqTrigger,
|
||||
}
|
||||
|
||||
pub trait InterruptTable {
|
||||
fn handler(&self, index: usize) -> Option<&'static dyn InterruptHandler>;
|
||||
}
|
||||
|
||||
pub trait ExternalInterruptController {
|
||||
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
|
||||
/// fired
|
||||
fn register_irq(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
options: IrqOptions,
|
||||
handler: &'static dyn InterruptHandler,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Enables the specified IRQ (unmasks it)
|
||||
fn enable_irq(&self, irq: IrqNumber) -> Result<(), Error>;
|
||||
|
||||
/// Handles a single pending interrupt on this controller.
|
||||
/// The function is intended for interrupt controllers which have internal registers to track
|
||||
/// the IRQ index and the order of interrupt handling (if multiple are handled in sequence) is
|
||||
/// platform/controller specific.
|
||||
fn handle_pending_irqs(&self) {}
|
||||
/// Handles a single pending interrupt with a known index on this controller.
|
||||
/// The function is intended for interrupt controllers where vectors "know" their interrupt
|
||||
/// index.
|
||||
fn handle_specific_irq(&self, #[allow(unused)] index: usize) {}
|
||||
}
|
||||
|
||||
pub trait LocalInterruptController {
|
||||
type IpiMessage;
|
||||
|
||||
fn send_ipi(&self, target: IpiDeliveryTarget, msg: Self::IpiMessage) -> Result<(), Error>;
|
||||
|
||||
/// Initializes the local interrupt controller for an Application Processor instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure this function is only called once per each AP (and only for APs).
|
||||
unsafe fn init_ap(&self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait InterruptHandler: Device {
|
||||
fn handle_irq(&self) -> bool;
|
||||
}
|
||||
|
||||
pub struct FixedInterruptTable<const SIZE: usize> {
|
||||
entries: [Option<&'static dyn InterruptHandler>; SIZE],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> FixedInterruptTable<SIZE> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
entries: [None; SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
index: usize,
|
||||
handler: &'static dyn InterruptHandler,
|
||||
) -> Result<(), Error> {
|
||||
if self.entries[index].is_some() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
self.entries[index] = Some(handler);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> InterruptTable for FixedInterruptTable<SIZE> {
|
||||
fn handler(&self, index: usize) -> Option<&'static dyn InterruptHandler> {
|
||||
self.entries[index]
|
||||
}
|
||||
}
|
24
lib/device-api/src/lib.rs
Normal file
24
lib/device-api/src/lib.rs
Normal file
@ -0,0 +1,24 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod bus;
|
||||
pub mod device;
|
||||
pub mod interrupt;
|
||||
pub mod manager;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
|
||||
pub use device::{Device, DeviceId};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub trait CpuBringupDevice: Device {
|
||||
/// Starts a CPU with given index, providing it with some argument value and instruction
|
||||
/// pointer from which its execution should begin.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it can have unexpected effects on the system state if
|
||||
/// misused.
|
||||
unsafe fn start_cpu(&self, id: usize, ip: usize, arg0: usize) -> Result<(), Error>;
|
||||
}
|
25
lib/device-api/src/manager.rs
Normal file
25
lib/device-api/src/manager.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::{Device, DeviceId};
|
||||
|
||||
pub struct DeviceManager {
|
||||
devices: Vec<&'static dyn Device>,
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
devices: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(&mut self, device: &'static dyn Device) -> DeviceId {
|
||||
let id = DeviceId::from(self.devices.len());
|
||||
self.devices.push(device);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn devices(&self) -> impl Iterator<Item = &'static dyn Device> + '_ {
|
||||
self.devices.iter().copied()
|
||||
}
|
||||
}
|
7
lib/device-api/src/serial.rs
Normal file
7
lib/device-api/src/serial.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::Device;
|
||||
|
||||
pub trait SerialDevice: Device {
|
||||
fn send(&self, byte: u8) -> Result<(), Error>;
|
||||
}
|
31
lib/device-api/src/timer.rs
Normal file
31
lib/device-api/src/timer.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//! Interfaces for time-providing devices
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::Device;
|
||||
|
||||
/// Interface for precise timing devices
|
||||
pub trait MonotonicTimestampProviderDevice: Device {
|
||||
/// Provides a timestamp value of the timer. The value:
|
||||
///
|
||||
/// * Represents monotonically increasing clock time since some arbitrary point in the past.
|
||||
/// * Can be used for delays and measuring time passed between two measurements.
|
||||
///
|
||||
/// * Is not an accurate wall-clock time or real-world time.
|
||||
/// * Cannot be used for date/time managament purposes.
|
||||
fn monotonic_timestamp(&self) -> Result<Duration, Error>;
|
||||
}
|
||||
|
||||
/// Interface for real-world time-telling devices
|
||||
pub trait RealTimeProviderDevice: Device {
|
||||
/// Provides a real-time clock value of the timer. The value:
|
||||
///
|
||||
/// * Represents a real-world time since TODO TODO TODO.
|
||||
/// * Can be used for rough measurements of duration passed between two points in time.
|
||||
/// * Can be used for delays, but precision is not guaranteed.
|
||||
/// * Can be used for date/time management.
|
||||
// TODO actual type for time
|
||||
fn real_timestamp(&self) -> Result<u64, Error>;
|
||||
}
|
@ -13,7 +13,32 @@
|
||||
movk \reg, #((\value) >> 16), lsl #16
|
||||
.endm
|
||||
|
||||
.macro LEAVE_EL2, ret_label
|
||||
mrs x8, CNTHCTL_EL2
|
||||
orr x8, x8, #(CNTHCTL_EL2_EL1PCTEN | CNTHCTL_EL2_EL1PCEN)
|
||||
msr CNTHCTL_EL2, x8
|
||||
msr CNTVOFF_EL2, xzr
|
||||
|
||||
MOV_L x8, SCTLR_EL2_RES1
|
||||
msr SCTLR_EL2, x8
|
||||
|
||||
mov x8, #HCR_EL2_RW_EL1IsAArch64
|
||||
msr HCR_EL2, x8
|
||||
|
||||
mov x8, #SPSR_EL2_EL1h
|
||||
orr x8, x8, #SPSR_EL2_MASK_DAIF
|
||||
msr SPSR_EL2, x8
|
||||
|
||||
adr x8, \ret_label
|
||||
msr ELR_EL2, x8
|
||||
|
||||
isb
|
||||
eret
|
||||
.endm
|
||||
|
||||
.global __aarch64_entry
|
||||
.global __aarch64_ap_entry
|
||||
|
||||
.section .text.entry
|
||||
__aarch64_entry:
|
||||
// x0 -- dtb_phys
|
||||
@ -30,26 +55,7 @@ __aarch64_entry:
|
||||
|
||||
// Leave EL2
|
||||
.el2:
|
||||
mrs x8, CNTHCTL_EL2
|
||||
orr x8, x8, #(CNTHCTL_EL2_EL1PCTEN | CNTHCTL_EL2_EL1PCEN)
|
||||
msr CNTHCTL_EL2, x8
|
||||
msr CNTVOFF_EL2, xzr
|
||||
|
||||
MOV_L x8, SCTLR_EL2_RES1
|
||||
msr SCTLR_EL2, x8
|
||||
|
||||
mov x8, #HCR_EL2_RW_EL1IsAArch64
|
||||
msr HCR_EL2, x8
|
||||
|
||||
mov x8, #SPSR_EL2_EL1h
|
||||
orr x8, x8, #SPSR_EL2_MASK_DAIF
|
||||
msr SPSR_EL2, x8
|
||||
|
||||
adr x8, .el1
|
||||
msr ELR_EL2, x8
|
||||
|
||||
isb
|
||||
eret
|
||||
LEAVE_EL2 .el1
|
||||
.el1:
|
||||
dsb sy
|
||||
isb
|
||||
@ -75,3 +81,24 @@ __aarch64_entry:
|
||||
// TODO spin loop for this method of init
|
||||
1:
|
||||
b .
|
||||
|
||||
.section .text
|
||||
__aarch64_ap_entry:
|
||||
// x0 -- stack pointer (lower address space)
|
||||
|
||||
mrs x8, CurrentEL
|
||||
lsr x8, x8, #2
|
||||
cmp x8, #2
|
||||
bne .ap_el1
|
||||
|
||||
.ap_el2:
|
||||
LEAVE_EL2 .ap_el1
|
||||
|
||||
.ap_el1:
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
mov sp, x0
|
||||
bl {kernel_ap_lower_entry} - {kernel_virt_offset}
|
||||
|
||||
b .
|
||||
|
@ -7,20 +7,15 @@ use core::{
|
||||
use aarch64_cpu::{asm::barrier, registers::CPACR_EL1};
|
||||
use tock_registers::interfaces::ReadWriteable;
|
||||
|
||||
use super::{
|
||||
cpu::Cpu, exception, kernel_main, smp::CPU_COUNT, AArch64, KernelStack, ARCHITECTURE,
|
||||
BOOT_STACK_SIZE,
|
||||
};
|
||||
use super::{cpu::Cpu, exception, kernel_main, KernelStack, ARCHITECTURE, BOOT_STACK_SIZE};
|
||||
use crate::{
|
||||
absolute_address,
|
||||
arch::{Architecture, PLATFORM},
|
||||
device::platform::Platform,
|
||||
arch::{aarch64::smp::CPU_COUNT, Architecture, ArchitectureImpl},
|
||||
mem::{ConvertAddress, KERNEL_VIRT_OFFSET},
|
||||
sync::SpinFence,
|
||||
task,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(super) static CPU_INIT_FENCE: SpinFence = SpinFence::new();
|
||||
|
||||
extern "C" fn el1_bsp_lower_entry(dtb_phys: usize) -> ! {
|
||||
@ -39,6 +34,22 @@ extern "C" fn el1_bsp_lower_entry(dtb_phys: usize) -> ! {
|
||||
enter_higher_half(sp as usize, elr, dtb_phys);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn el1_ap_lower_entry(sp: usize) -> ! {
|
||||
ArchitectureImpl::set_interrupt_mask(true);
|
||||
|
||||
// Unmask FP operations
|
||||
CPACR_EL1.modify(CPACR_EL1::FPEN::TrapNothing);
|
||||
|
||||
unsafe {
|
||||
ARCHITECTURE.init_mmu(false);
|
||||
}
|
||||
|
||||
let sp = sp.virtualize();
|
||||
let elr = absolute_address!(__aarch64_ap_upper_entry);
|
||||
|
||||
enter_higher_half(sp, elr, 0);
|
||||
}
|
||||
|
||||
fn enter_higher_half(sp: usize, elr: usize, arg: usize) -> ! {
|
||||
unsafe {
|
||||
asm!(r#"
|
||||
@ -49,29 +60,12 @@ fn enter_higher_half(sp: usize, elr: usize, arg: usize) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) extern "C" fn __aarch64_ap_lower_entry(_sp: usize) -> ! {
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
// __aarch64_common_lower_entry();
|
||||
|
||||
// unsafe {
|
||||
// ARCHITECTURE.init_mmu(false);
|
||||
// }
|
||||
|
||||
// let sp = unsafe { sp.virtualize() };
|
||||
// let elr = absolute_address!(__aarch64_ap_upper_entry);
|
||||
// enter_higher_half(sp, elr, 0);
|
||||
}
|
||||
|
||||
extern "C" fn __aarch64_bsp_upper_entry(dtb_phys: usize) -> ! {
|
||||
kernel_main(dtb_phys);
|
||||
}
|
||||
|
||||
extern "C" fn __aarch64_ap_upper_entry(_x0: usize) -> ! {
|
||||
unsafe {
|
||||
AArch64::set_interrupt_mask(true);
|
||||
}
|
||||
assert!(ArchitectureImpl::interrupt_mask());
|
||||
|
||||
// Signal to BSP that we're up
|
||||
CPU_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
@ -81,7 +75,7 @@ extern "C" fn __aarch64_ap_upper_entry(_x0: usize) -> ! {
|
||||
|
||||
// Initialize CPU-local GIC and timer
|
||||
unsafe {
|
||||
PLATFORM.init(false).expect("AP platform init failed");
|
||||
ARCHITECTURE.init_platform(false);
|
||||
|
||||
Cpu::init_local();
|
||||
|
||||
@ -100,6 +94,7 @@ static BSP_STACK: KernelStack = KernelStack {
|
||||
global_asm!(
|
||||
include_str!("entry.S"),
|
||||
kernel_lower_entry = sym el1_bsp_lower_entry,
|
||||
kernel_ap_lower_entry = sym el1_ap_lower_entry,
|
||||
stack_bottom = sym BSP_STACK,
|
||||
stack_size = const BOOT_STACK_SIZE,
|
||||
kernel_virt_offset = const KERNEL_VIRT_OFFSET
|
||||
|
@ -88,7 +88,7 @@ impl TaskContextImpl for TaskContext {
|
||||
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
|
||||
|
||||
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
|
||||
const KERNEL_TASK_PAGES: usize = 4;
|
||||
const KERNEL_TASK_PAGES: usize = 8;
|
||||
let stack_base = unsafe {
|
||||
phys::alloc_pages_contiguous(KERNEL_TASK_PAGES, PageUsage::Used)?.virtualize()
|
||||
};
|
||||
|
@ -5,10 +5,14 @@ use aarch64_cpu::registers::{MPIDR_EL1, TPIDR_EL1};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
use crate::{arch::CpuMessage, sync::IrqSafeSpinlock, task::sched::CpuQueue, util::OneTimeInit};
|
||||
use crate::{
|
||||
arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue, util::OneTimeInit,
|
||||
};
|
||||
|
||||
use super::smp::CPU_COUNT;
|
||||
|
||||
// use super::smp::CPU_COUNT;
|
||||
|
||||
/// Per-CPU private data structure
|
||||
#[repr(C, align(0x10))]
|
||||
pub struct Cpu {
|
||||
@ -108,4 +112,17 @@ impl Cpu {
|
||||
(0..CPU_COUNT.load(Ordering::Acquire)).map(|_| IpiQueue::new()),
|
||||
));
|
||||
}
|
||||
|
||||
/// Gets an IPI message from the processor's queue and takes corresponding actions. If there is
|
||||
/// none, this is treated as a spurious IPI and ignored. See [CpuMessage].
|
||||
pub fn handle_ipi(&self) {
|
||||
let Some(msg) = self.get_ipi() else {
|
||||
warnln!("Spurious IPI in cpu{}", self.id);
|
||||
return;
|
||||
};
|
||||
|
||||
match msg {
|
||||
CpuMessage::Panic => panic::panic_secondary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +40,49 @@ pub trait DevTreeIndexPropExt {
|
||||
pub trait DevTreeIndexNodeExt {
|
||||
/// Returns the root node's `#address-cells` property, or the default value defined by the
|
||||
/// specification if it's absent
|
||||
fn address_cells(&self) -> usize;
|
||||
fn address_cells(&self) -> usize {
|
||||
self.get_address_cells().unwrap_or(2)
|
||||
}
|
||||
/// Returns the root node's `#size-cells` property, or the default value defined by the
|
||||
/// specification if it's absent
|
||||
fn size_cells(&self) -> usize;
|
||||
fn size_cells(&self) -> usize {
|
||||
self.get_size_cells().unwrap_or(1)
|
||||
}
|
||||
|
||||
/// Returns the #address-cells property of the node, if there is one
|
||||
fn get_address_cells(&self) -> Option<usize>;
|
||||
/// Returns the #size-cells property of the node, if there is one
|
||||
fn get_size_cells(&self) -> Option<usize>;
|
||||
}
|
||||
|
||||
/// Extension trait for [DevTreeIndexNode] to obtain typed property values
|
||||
pub trait DevTreeIndexNodePropGet<T> {
|
||||
/// Returns a property value of given type, if it exists
|
||||
fn prop(&self, name: &str) -> Option<T>;
|
||||
}
|
||||
|
||||
impl<'a, 'i, 'dt> DevTreeIndexNodePropGet<u32> for DevTreeIndexNode<'a, 'i, 'dt> {
|
||||
fn prop(&self, name: &str) -> Option<u32> {
|
||||
self.props().find_map(|prop| {
|
||||
if prop.name().ok()? == name {
|
||||
prop.u32(0).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'i, 'dt> DevTreeIndexNodePropGet<&'a str> for DevTreeIndexNode<'a, 'i, 'dt> {
|
||||
fn prop(&self, name: &str) -> Option<&'a str> {
|
||||
self.props().find_map(|prop| {
|
||||
if prop.name().ok()? == name {
|
||||
prop.str().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator for physical memory regions present in the device tree
|
||||
@ -98,21 +137,24 @@ impl<'a> DeviceTree<'a> {
|
||||
pub fn size_cells(&self) -> usize {
|
||||
self.index.root().size_cells()
|
||||
}
|
||||
|
||||
/// Returns the root node of the device tree
|
||||
pub fn root(&self) -> DevTreeIndexNode {
|
||||
self.index.root()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'i, 'dt> DevTreeIndexNodeExt for DevTreeIndexNode<'a, 'i, 'dt> {
|
||||
fn address_cells(&self) -> usize {
|
||||
fn get_address_cells(&self) -> Option<usize> {
|
||||
self.props()
|
||||
.find(|p| p.name().unwrap_or("") == "#address-cells")
|
||||
.map(|p| p.u32(0).unwrap() as usize)
|
||||
.unwrap_or(2)
|
||||
}
|
||||
|
||||
fn size_cells(&self) -> usize {
|
||||
fn get_size_cells(&self) -> Option<usize> {
|
||||
self.props()
|
||||
.find(|p| p.name().unwrap_or("") == "#size-cells")
|
||||
.map(|p| p.u32(0).unwrap() as usize)
|
||||
.unwrap_or(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,14 +10,14 @@ use abi::{
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::cpu::Cpu, CpuMessage, PLATFORM},
|
||||
arch::{aarch64::cpu::Cpu, Architecture},
|
||||
debug::LogLevel,
|
||||
device::interrupt::IrqContext,
|
||||
panic::panic_secondary,
|
||||
syscall::raw_syscall_handler,
|
||||
task::{context::TaskFrame, process::Process},
|
||||
};
|
||||
|
||||
use super::ARCHITECTURE;
|
||||
|
||||
/// Struct for register values saved when taking an exception
|
||||
#[repr(C)]
|
||||
pub struct ExceptionFrame {
|
||||
@ -202,8 +202,19 @@ extern "C" fn __aa64_el0_serror_handler() {
|
||||
|
||||
// EL1
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_el1_sync_handler(_frame: *mut ExceptionFrame) {
|
||||
todo!();
|
||||
extern "C" fn __aa64_el1_sync_handler(frame: *mut ExceptionFrame) {
|
||||
let frame = unsafe { &*frame };
|
||||
let esr_el1 = ESR_EL1.get();
|
||||
let ec = (esr_el1 >> 26) & 0x3F;
|
||||
let iss = esr_el1 & 0x1FFFFFF;
|
||||
|
||||
unsafe {
|
||||
crate::sync::hack_locks();
|
||||
}
|
||||
|
||||
dump_irrecoverable_exception(frame, ec, iss);
|
||||
|
||||
panic!("Irrecoverable exception in kernel mode");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -270,10 +281,9 @@ fn el0_sync_inner(frame: &mut ExceptionFrame) {
|
||||
}
|
||||
|
||||
fn irq_common() {
|
||||
unsafe {
|
||||
let ic = IrqContext::new();
|
||||
PLATFORM.gic.handle_pending_irqs(&ic);
|
||||
}
|
||||
ARCHITECTURE
|
||||
.external_interrupt_controller()
|
||||
.handle_pending_irqs();
|
||||
}
|
||||
|
||||
unsafe fn handle_signal_exit(frame: &mut ExceptionFrame) {
|
||||
@ -288,15 +298,4 @@ unsafe fn handle_signal_exit(frame: &mut ExceptionFrame) {
|
||||
frame.restore(&saved_data.frame);
|
||||
}
|
||||
|
||||
pub(super) fn ipi_handler(msg: Option<CpuMessage>) {
|
||||
if let Some(msg) = msg {
|
||||
match msg {
|
||||
CpuMessage::Panic => panic_secondary(),
|
||||
}
|
||||
} else {
|
||||
warnln!("Spurious IPI received by cpu{}", Cpu::local_id());
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
global_asm!(include_str!("vectors.S"));
|
||||
|
@ -5,7 +5,7 @@ use tock_registers::{
|
||||
registers::ReadWrite,
|
||||
};
|
||||
|
||||
use crate::{device::interrupt::IrqContext, mem::device::DeviceMemoryIo};
|
||||
use crate::mem::device::DeviceMemoryIo;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@ -50,11 +50,11 @@ impl Gicc {
|
||||
self.regs.PMR.write(PMR::Priority.val(0xFF));
|
||||
}
|
||||
|
||||
pub fn pending_irq_number<'irq>(&'irq self, _ic: &IrqContext<'irq>) -> usize {
|
||||
pub fn pending_irq_number(&self) -> usize {
|
||||
self.regs.IAR.read(IAR::InterruptID) as usize
|
||||
}
|
||||
|
||||
pub fn clear_irq<'irq>(&'irq self, irq: usize, _ic: &IrqContext<'irq>) {
|
||||
pub fn clear_irq(&self, irq: usize) {
|
||||
self.regs.EOIR.write(EOIR::EOINTID.val(irq as u32));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! ARM GICv2 Distributor registers
|
||||
use device_api::interrupt::IpiDeliveryTarget;
|
||||
use spinning_top::Spinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
@ -6,9 +7,7 @@ use tock_registers::{
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
use crate::{device::interrupt::IpiDeliveryTarget, mem::device::DeviceMemoryIo};
|
||||
|
||||
use super::IrqNumber;
|
||||
use crate::mem::device::DeviceMemoryIo;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@ -100,11 +99,12 @@ impl Gicd {
|
||||
pub unsafe fn set_sgir(&self, target: IpiDeliveryTarget, interrupt_id: u64) {
|
||||
assert_eq!(interrupt_id & !0xF, 0);
|
||||
let value = match target {
|
||||
IpiDeliveryTarget::AllExceptLocal => SGIR::TargetListFilter::AllExceptLocal,
|
||||
IpiDeliveryTarget::Specified(_mask) => {
|
||||
IpiDeliveryTarget::OtherCpus => SGIR::TargetListFilter::AllExceptLocal,
|
||||
IpiDeliveryTarget::Specific(_mask) => {
|
||||
// TODO: need to handle self-ipi case, releasing the lock somehow
|
||||
todo!();
|
||||
}
|
||||
IpiDeliveryTarget::ThisCpu => todo!(),
|
||||
} + SGIR::INTID.val(interrupt_id as u32);
|
||||
|
||||
self.shared_regs.lock().SGIR.write(value);
|
||||
@ -114,7 +114,7 @@ impl Gicd {
|
||||
self.banked_regs.ITARGETSR[0].read(ITARGETSR::Offset0)
|
||||
}
|
||||
|
||||
fn enable_irq_inner(&self, irq: usize) {
|
||||
pub fn enable_irq(&self, irq: usize) {
|
||||
let reg = irq >> 5;
|
||||
let bit = 1u32 << (irq & 0x1F);
|
||||
|
||||
@ -135,12 +135,6 @@ impl Gicd {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_irq(&self, irq: IrqNumber) {
|
||||
let irq = irq.get();
|
||||
|
||||
self.enable_irq_inner(irq);
|
||||
}
|
||||
|
||||
pub unsafe fn init(&self) {
|
||||
let mask = self.local_gic_target_mask();
|
||||
let regs = self.shared_regs.lock();
|
||||
|
@ -1,20 +1,32 @@
|
||||
//! ARM Generic Interrupt Controller v2 driver
|
||||
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use aarch64_cpu::asm::barrier;
|
||||
use abi::error::Error;
|
||||
use spinning_top::Spinlock;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
|
||||
IpiDeliveryTarget, IrqNumber, IrqOptions, LocalInterruptController,
|
||||
},
|
||||
Device,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
device::{
|
||||
interrupt::{ExternalInterruptController, InterruptSource, IrqContext},
|
||||
Device, InitializableDevice,
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexPropExt},
|
||||
Architecture, CpuMessage,
|
||||
},
|
||||
device_tree_driver,
|
||||
mem::device::{DeviceMemory, DeviceMemoryIo},
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use self::{gicc::Gicc, gicd::Gicd};
|
||||
|
||||
use super::{cpu::Cpu, exception::ipi_handler};
|
||||
use super::{cpu::Cpu, smp::CPU_COUNT, ARCHITECTURE};
|
||||
|
||||
const MAX_IRQ: usize = 300;
|
||||
const IPI_VECTOR: u64 = 1;
|
||||
@ -22,49 +34,21 @@ const IPI_VECTOR: u64 = 1;
|
||||
pub mod gicc;
|
||||
pub mod gicd;
|
||||
|
||||
/// Wrapper type for ARM interrupt vector
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IrqNumber(usize);
|
||||
|
||||
/// ARM Generic Interrupt Controller v2
|
||||
pub struct Gic {
|
||||
gicc: OneTimeInit<Gicc>,
|
||||
gicd: OneTimeInit<Gicd>,
|
||||
gicd_base: usize,
|
||||
gicc_base: usize,
|
||||
irq_table: Spinlock<[Option<&'static (dyn InterruptSource + Sync)>; MAX_IRQ]>,
|
||||
}
|
||||
|
||||
impl IrqNumber {
|
||||
/// Returns the underlying vector number
|
||||
#[inline(always)]
|
||||
pub const fn get(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Wraps the interrupt vector value in the [IrqNumber] type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `v` is not a valid interrupt number.
|
||||
#[inline(always)]
|
||||
pub const fn new(v: usize) -> Self {
|
||||
assert!(v < MAX_IRQ);
|
||||
Self(v)
|
||||
}
|
||||
table: IrqSafeSpinlock<FixedInterruptTable<MAX_IRQ>>,
|
||||
}
|
||||
|
||||
impl Device for Gic {
|
||||
fn name(&self) -> &'static str {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"ARM Generic Interrupt Controller v2"
|
||||
}
|
||||
}
|
||||
|
||||
impl InitializableDevice for Gic {
|
||||
type Options = ();
|
||||
|
||||
unsafe fn init(&self, _opts: ()) -> Result<(), Error> {
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
let gicd_mmio = DeviceMemory::map("GICv2 Distributor registers", self.gicd_base, 0x1000)?;
|
||||
let gicd_mmio_shared = DeviceMemoryIo::new(gicd_mmio.clone());
|
||||
let gicd_mmio_banked = DeviceMemoryIo::new(gicd_mmio);
|
||||
@ -79,66 +63,104 @@ impl InitializableDevice for Gic {
|
||||
self.gicd.init(gicd);
|
||||
self.gicc.init(gicc);
|
||||
|
||||
ARCHITECTURE.register_external_interrupt_controller(self)?;
|
||||
ARCHITECTURE.register_local_interrupt_controller(self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalInterruptController for Gic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> {
|
||||
self.gicd.get().enable_irq(irq);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
fn register_irq(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn InterruptSource + Sync),
|
||||
irq: IrqNumber,
|
||||
_options: IrqOptions,
|
||||
handler: &'static dyn InterruptHandler,
|
||||
) -> Result<(), Error> {
|
||||
let mut table = self.irq_table.lock();
|
||||
let irq = irq.get();
|
||||
if table[irq].is_some() {
|
||||
return Err(Error::AlreadyExists);
|
||||
}
|
||||
let mut table = self.table.lock();
|
||||
|
||||
debugln!("Bound irq{} to {:?}", irq, Device::name(handler));
|
||||
table[irq] = Some(handler);
|
||||
let index = match irq {
|
||||
IrqNumber::Shared(i) => i + 32,
|
||||
IrqNumber::Private(i) => i + 16,
|
||||
} as usize;
|
||||
|
||||
debugln!("Bound irq{} to {:?}", index, handler.display_name());
|
||||
table.insert(index, handler)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_irq(
|
||||
&self,
|
||||
_irq: Self::IrqNumber,
|
||||
_active_low: bool,
|
||||
_level_triggered: bool,
|
||||
) -> Result<(), Error> {
|
||||
todo!()
|
||||
fn enable_irq(&self, irq: IrqNumber) -> Result<(), Error> {
|
||||
let gicd = self.gicd.get();
|
||||
let index = match irq {
|
||||
IrqNumber::Shared(i) => i + 32,
|
||||
IrqNumber::Private(i) => i + 16,
|
||||
} as usize;
|
||||
gicd.enable_irq(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error> {
|
||||
// // TODO message queue insertion should be moved
|
||||
// match target {
|
||||
// IpiDeliveryTarget::AllExceptLocal => {
|
||||
// let local = Cpu::local_id();
|
||||
// for i in 0..CPU_COUNT.load(Ordering::Acquire) {
|
||||
// if i != local as usize {
|
||||
// Cpu::push_ipi_queue(i as u32, msg);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// IpiDeliveryTarget::Specified(_) => todo!(),
|
||||
// }
|
||||
fn handle_pending_irqs(&self) {
|
||||
let gicc = self.gicc.get();
|
||||
let irq_number = gicc.pending_irq_number();
|
||||
if irq_number >= MAX_IRQ {
|
||||
return;
|
||||
}
|
||||
|
||||
// // Issue a memory barrier
|
||||
// barrier::dsb(barrier::ISH);
|
||||
// barrier::isb(barrier::SY);
|
||||
if irq_number == IPI_VECTOR as usize {
|
||||
gicc.clear_irq(irq_number);
|
||||
Cpu::local().handle_ipi();
|
||||
return;
|
||||
}
|
||||
|
||||
// self.gicd.get().set_sgir(target, IPI_VECTOR);
|
||||
gicc.clear_irq(irq_number);
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
{
|
||||
let table = self.table.lock();
|
||||
match table.handler(irq_number) {
|
||||
Some(handler) => {
|
||||
drop(table);
|
||||
handler.handle_irq();
|
||||
}
|
||||
None => warnln!("No handler for irq{}", irq_number),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalInterruptController for Gic {
|
||||
type IpiMessage = CpuMessage;
|
||||
|
||||
fn send_ipi(&self, target: IpiDeliveryTarget, msg: Self::IpiMessage) -> Result<(), Error> {
|
||||
// TODO message queue insertion should be moved
|
||||
match target {
|
||||
IpiDeliveryTarget::OtherCpus => {
|
||||
let local = Cpu::local_id();
|
||||
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!(),
|
||||
}
|
||||
|
||||
// Issue a memory barrier
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
unsafe {
|
||||
self.gicd.get().set_sgir(target, IPI_VECTOR);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_ap(&self) -> Result<(), Error> {
|
||||
self.gicc.get().init();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Gic {
|
||||
@ -153,46 +175,19 @@ impl Gic {
|
||||
gicd: OneTimeInit::new(),
|
||||
gicd_base,
|
||||
gicc_base,
|
||||
irq_table: Spinlock::new([None; MAX_IRQ]),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes GICv2 for an application processor.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must not be called more than once per each AP. Must not be called from BSP.
|
||||
pub unsafe fn init_smp_ap(&self) -> Result<(), Error> {
|
||||
self.gicc.get().init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles a single pending IRQ (if there're many, the CPU will just get here again)
|
||||
pub fn handle_pending_irqs<'irq>(&'irq self, ic: &IrqContext<'irq>) {
|
||||
let gicc = self.gicc.get();
|
||||
let irq_number = gicc.pending_irq_number(ic);
|
||||
if irq_number >= MAX_IRQ {
|
||||
return;
|
||||
}
|
||||
|
||||
gicc.clear_irq(irq_number, ic);
|
||||
|
||||
if irq_number as u64 == IPI_VECTOR {
|
||||
// IPI received
|
||||
let msg = Cpu::local().get_ipi();
|
||||
ipi_handler(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let table = self.irq_table.lock();
|
||||
match table[irq_number] {
|
||||
None => panic!("No IRQ handler registered for irq{}", irq_number),
|
||||
Some(handler) => {
|
||||
drop(table);
|
||||
handler.handle_irq().expect("IRQ handler failed");
|
||||
}
|
||||
}
|
||||
table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["arm,cortex-a15-gic", "arm,gic-400"],
|
||||
probe(dt) => {
|
||||
let reg = devtree::find_prop(&dt.node, "reg")?;
|
||||
|
||||
let (gicc_base, _) = reg.cell2_array_item(0, dt.address_cells, dt.size_cells)?;
|
||||
let (gicd_base, _) = reg.cell2_array_item(1, dt.address_cells, dt.size_cells)?;
|
||||
|
||||
Some(Box::new(unsafe { Gic::new(gicc_base as usize, gicd_base as usize) }))
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,27 @@
|
||||
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use aarch64_cpu::registers::{DAIF, ID_AA64MMFR0_EL1, SCTLR_EL1, TCR_EL1, TTBR0_EL1, TTBR1_EL1};
|
||||
use aarch64_cpu::registers::{
|
||||
CNTP_CTL_EL0, CNTP_TVAL_EL0, DAIF, ID_AA64MMFR0_EL1, SCTLR_EL1, TCR_EL1, TTBR0_EL1, TTBR1_EL1,
|
||||
};
|
||||
use abi::error::Error;
|
||||
use cfg_if::cfg_if;
|
||||
use device_api::{
|
||||
interrupt::{
|
||||
ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController,
|
||||
},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
use git_version::git_version;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::{cpu::Cpu, devtree::FdtMemoryRegionIter, smp::CPU_COUNT},
|
||||
aarch64::{cpu::Cpu, devtree::FdtMemoryRegionIter},
|
||||
Architecture,
|
||||
},
|
||||
debug,
|
||||
device::platform::Platform,
|
||||
debug::{self},
|
||||
device::{self, power::arm_psci::Psci, DevTreeNodeInfo},
|
||||
fs::{devfs, Initrd, INITRD_DATA},
|
||||
mem::{
|
||||
heap,
|
||||
@ -25,23 +34,13 @@ use crate::{
|
||||
};
|
||||
|
||||
use self::{
|
||||
boot::CPU_INIT_FENCE,
|
||||
devtree::{DevTreeIndexPropExt, DeviceTree},
|
||||
smp::CPU_COUNT,
|
||||
table::{init_fixed_tables, KERNEL_TABLES},
|
||||
};
|
||||
|
||||
pub mod plat_qemu;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "aarch64_qemu")] {
|
||||
pub use plat_qemu::{PlatformImpl, PLATFORM};
|
||||
} else if #[cfg(feature = "aarch64_orange_pi3")] {
|
||||
pub mod plat_orange_pi3;
|
||||
|
||||
pub use plat_orange_pi3::{PlatformImpl, PLATFORM};
|
||||
} else {
|
||||
compile_error!("No platform selected for AArch64");
|
||||
}
|
||||
}
|
||||
use super::CpuMessage;
|
||||
|
||||
pub mod boot;
|
||||
pub mod context;
|
||||
@ -64,11 +63,24 @@ struct KernelStack {
|
||||
/// AArch64 platform interface
|
||||
pub struct AArch64 {
|
||||
dt: OneTimeInit<DeviceTree<'static>>,
|
||||
ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>,
|
||||
local_intc: OneTimeInit<&'static dyn LocalInterruptController<IpiMessage = CpuMessage>>,
|
||||
mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>,
|
||||
|
||||
// ARM-only devices
|
||||
/// ARM PSCI instance on this system (there may not be one)
|
||||
pub psci: OneTimeInit<&'static Psci>,
|
||||
}
|
||||
|
||||
/// Global platform handle
|
||||
pub static ARCHITECTURE: AArch64 = AArch64 {
|
||||
dt: OneTimeInit::new(),
|
||||
ext_intc: OneTimeInit::new(),
|
||||
local_intc: OneTimeInit::new(),
|
||||
mtimer: OneTimeInit::new(),
|
||||
|
||||
// ARM-only devices
|
||||
psci: OneTimeInit::new(),
|
||||
};
|
||||
|
||||
impl Architecture for AArch64 {
|
||||
@ -123,6 +135,56 @@ impl Architecture for AArch64 {
|
||||
fn cpu_count() -> usize {
|
||||
CPU_COUNT.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn register_external_interrupt_controller(
|
||||
&self,
|
||||
intc: &'static dyn ExternalInterruptController,
|
||||
) -> Result<(), Error> {
|
||||
self.ext_intc.init(intc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_local_interrupt_controller(
|
||||
&self,
|
||||
intc: &'static dyn LocalInterruptController<IpiMessage = super::CpuMessage>,
|
||||
) -> Result<(), Error> {
|
||||
self.local_intc.init(intc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_monotonic_timer(
|
||||
&self,
|
||||
timer: &'static dyn MonotonicTimestampProviderDevice,
|
||||
) -> Result<(), Error> {
|
||||
self.mtimer.init(timer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController {
|
||||
*self.ext_intc.get()
|
||||
}
|
||||
|
||||
fn local_interrupt_controller(
|
||||
&self,
|
||||
) -> &'static dyn LocalInterruptController<IpiMessage = super::CpuMessage> {
|
||||
*self.local_intc.get()
|
||||
}
|
||||
|
||||
fn monotonic_timer(&self) -> &'static dyn MonotonicTimestampProviderDevice {
|
||||
*self.mtimer.get()
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error> {
|
||||
if let Some(local_intc) = self.local_intc.try_get() {
|
||||
local_intc.send_ipi(target, msg)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn reset(&self) -> ! {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AArch64 {
|
||||
@ -170,6 +232,109 @@ impl AArch64 {
|
||||
|
||||
phys::init_from_iter(regions)
|
||||
}
|
||||
|
||||
fn chosen_stdout_path<'a>(dt: &'a DeviceTree) -> Option<&'a str> {
|
||||
let chosen = dt.node_by_path("/chosen")?;
|
||||
let prop = devtree::find_prop(&chosen, "stdout-path")?;
|
||||
prop.str().ok()
|
||||
}
|
||||
|
||||
fn init_platform(&self, bsp: bool) {
|
||||
if bsp {
|
||||
let dt = self.device_tree();
|
||||
|
||||
let address_cells = dt.address_cells();
|
||||
let size_cells = dt.size_cells();
|
||||
|
||||
let chosen_stdout_path = Self::chosen_stdout_path(dt);
|
||||
let chosen_stdout = chosen_stdout_path.and_then(|path| dt.node_by_path(path));
|
||||
|
||||
// Probe and initialize the /chosen.stdout-path device first
|
||||
if let Some(node) = chosen_stdout.clone() {
|
||||
let probe = DevTreeNodeInfo {
|
||||
address_cells,
|
||||
size_cells,
|
||||
node,
|
||||
};
|
||||
|
||||
if let Some((device, _)) = device::probe_dt_node(&probe) {
|
||||
unsafe {
|
||||
device.init().unwrap();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
debug::reset();
|
||||
|
||||
// Print some stuff now that the output is initialized
|
||||
infoln!(
|
||||
"Yggdrasil v{} ({})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
git_version!()
|
||||
);
|
||||
infoln!("Initializing aarch64 platform");
|
||||
|
||||
// Probe and initialize the rest of devices
|
||||
let nodes = dt.root().children();
|
||||
if let Err(error) = device::enumerate_dt(
|
||||
address_cells,
|
||||
size_cells,
|
||||
nodes,
|
||||
|_, probe| {
|
||||
// Ignore /chosen/stdout-path node
|
||||
if let Some(ref chosen_stdout) = chosen_stdout && chosen_stdout.name() == probe.node.name() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some((device, _)) = device::probe_dt_node(&probe) {
|
||||
unsafe {
|
||||
device.init()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
warnln!(
|
||||
"{} errors encountered when initializing platform devices",
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
// Initialize IRQs for the devices
|
||||
device::manager_lock().devices().for_each(|dev| unsafe {
|
||||
if let Err(error) = dev.init_irq() {
|
||||
errorln!(
|
||||
"Could not init interrupts for {:?}: {:?}",
|
||||
dev.display_name(),
|
||||
error
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Print the device list
|
||||
infoln!("Enumerated devices:");
|
||||
device::manager_lock().devices().for_each(|dev| {
|
||||
infoln!("* {:?}", dev.display_name());
|
||||
});
|
||||
} else {
|
||||
// BSP already initialized everything needed
|
||||
// Setup timer and local interrupt controller
|
||||
let intc = self.local_intc.get();
|
||||
|
||||
unsafe {
|
||||
intc.init_ap().unwrap();
|
||||
}
|
||||
|
||||
// TODO device-tree initialization for this
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::CLEAR);
|
||||
CNTP_TVAL_EL0.set(10000000);
|
||||
self.ext_intc
|
||||
.get()
|
||||
.enable_irq(IrqNumber::Private(14))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_initrd() {
|
||||
@ -228,21 +393,15 @@ pub fn kernel_main(dtb_phys: usize) -> ! {
|
||||
TTBR0_EL1.set(0);
|
||||
|
||||
unsafe {
|
||||
AArch64::set_interrupt_mask(true);
|
||||
|
||||
PLATFORM.init_debug();
|
||||
ARCHITECTURE.init_device_tree(dtb_phys);
|
||||
}
|
||||
debug::reset();
|
||||
|
||||
AArch64::set_interrupt_mask(true);
|
||||
|
||||
exception::init_exceptions();
|
||||
|
||||
// Setup initrd
|
||||
setup_initrd();
|
||||
|
||||
debugln!("Initializing {} platform", PLATFORM.name());
|
||||
|
||||
unsafe {
|
||||
ARCHITECTURE
|
||||
.init_physical_memory(dtb_phys)
|
||||
.expect("Failed to initialize the physical memory manager");
|
||||
@ -252,27 +411,32 @@ pub fn kernel_main(dtb_phys: usize) -> ! {
|
||||
.expect("Could not allocate a block for heap");
|
||||
heap::init_heap(heap_base.virtualize(), 16 * 0x1000);
|
||||
|
||||
devfs::init();
|
||||
|
||||
// Enumerate the device tree
|
||||
ARCHITECTURE.init_platform(true);
|
||||
|
||||
debugln!("Heap: {:#x?}", heap::heap_range());
|
||||
|
||||
Cpu::init_local();
|
||||
|
||||
devfs::init();
|
||||
PLATFORM.init(true).unwrap();
|
||||
let dt = ARCHITECTURE.dt.get();
|
||||
if let Err(e) = smp::start_ap_cores(dt) {
|
||||
errorln!(
|
||||
"Could not initialize AP CPUs: {:?}. Will continue with one CPU.",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
// let dt = ARCHITECTURE.dt.get();
|
||||
// if let Err(e) = smp::start_ap_cores(dt) {
|
||||
// errorln!(
|
||||
// "Could not initialize AP CPUs: {:?}. Will continue with one CPU.",
|
||||
// e
|
||||
// );
|
||||
// }
|
||||
Cpu::init_ipi_queues();
|
||||
|
||||
// Cpu::init_ipi_queues();
|
||||
|
||||
// CPU_INIT_FENCE.signal();
|
||||
// CPU_INIT_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
|
||||
CPU_INIT_FENCE.signal();
|
||||
CPU_INIT_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
|
||||
|
||||
task::init().expect("Failed to initialize the scheduler");
|
||||
|
||||
// Initialize and enter the scheduler
|
||||
infoln!("All cpus ready");
|
||||
|
||||
task::enter();
|
||||
}
|
||||
}
|
||||
|
@ -35,29 +35,31 @@ impl Platform for PlatformImpl {
|
||||
const KERNEL_PHYS_BASE: usize = 0x40080000;
|
||||
|
||||
unsafe fn init(&'static self, is_bsp: bool) -> Result<(), Error> {
|
||||
if is_bsp {
|
||||
self.gic.init(())?;
|
||||
loop {}
|
||||
// if is_bsp {
|
||||
// self.gic.init(())?;
|
||||
|
||||
self.pl011.init_irq()?;
|
||||
devfs::add_char_device(&self.pl011, CharDeviceType::TtySerial)?;
|
||||
// self.pl011.init_irq()?;
|
||||
// devfs::add_char_device(&self.pl011, CharDeviceType::TtySerial)?;
|
||||
|
||||
self.local_timer.init(())?;
|
||||
self.local_timer.init_irq()?;
|
||||
} else {
|
||||
self.gic.init_smp_ap()?;
|
||||
// self.local_timer.init(())?;
|
||||
// self.local_timer.init_irq()?;
|
||||
// } else {
|
||||
// self.gic.init_smp_ap()?;
|
||||
|
||||
// TODO somehow merge this with the rest of the code
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::CLEAR);
|
||||
CNTP_TVAL_EL0.set(10000000);
|
||||
self.gic.enable_irq(IrqNumber::new(30))?;
|
||||
}
|
||||
// // TODO somehow merge this with the rest of the code
|
||||
// CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::CLEAR);
|
||||
// CNTP_TVAL_EL0.set(10000000);
|
||||
// self.gic.enable_irq(IrqNumber::new(30))?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
// Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_debug(&'static self) {
|
||||
self.pl011.init(()).ok();
|
||||
debug::add_sink(&self.pl011, LogLevel::Debug);
|
||||
loop {}
|
||||
// self.pl011.init(()).ok();
|
||||
// debug::add_sink(&self.pl011, LogLevel::Debug);
|
||||
}
|
||||
|
||||
fn interrupt_controller(
|
||||
|
@ -1,60 +1,79 @@
|
||||
//! Simultaneous multiprocessing support for aarch64
|
||||
use core::{
|
||||
arch::asm,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use device_api::CpuBringupDevice;
|
||||
use fdt_rs::prelude::PropReader;
|
||||
|
||||
use crate::{
|
||||
absolute_address,
|
||||
arch::aarch64::boot::__aarch64_ap_lower_entry,
|
||||
arch::ARCHITECTURE,
|
||||
mem::{
|
||||
phys::{self, PageUsage},
|
||||
ConvertAddress, KERNEL_VIRT_OFFSET,
|
||||
ConvertAddress,
|
||||
},
|
||||
};
|
||||
|
||||
use super::devtree::{self, DeviceTree};
|
||||
use super::devtree::{self, DevTreeIndexNodePropGet, DeviceTree};
|
||||
|
||||
/// ARM Power State Coordination Interface
|
||||
pub struct Psci {}
|
||||
#[derive(Debug)]
|
||||
enum CpuEnableMethod {
|
||||
Psci,
|
||||
// Not currently supported
|
||||
#[allow(dead_code)]
|
||||
SpinTable {
|
||||
release_addr: usize,
|
||||
},
|
||||
}
|
||||
|
||||
struct CpuInfo<'a> {
|
||||
id: u32,
|
||||
compatible: &'a str,
|
||||
enable_method: CpuEnableMethod,
|
||||
}
|
||||
|
||||
fn enumerate_cpus<'a>(dt: &'a DeviceTree) -> impl Iterator<Item = CpuInfo<'a>> {
|
||||
let cpus = dt.node_by_path("/cpus").unwrap();
|
||||
|
||||
cpus.children().filter_map(|cpu_node| {
|
||||
let compatible = devtree::find_prop(&cpu_node, "compatible").and_then(|p| p.str().ok())?;
|
||||
let id = cpu_node.prop("reg")?; // devtree::find_prop(&cpu_node, "reg").and_then(|p| p.u32(0).ok())?;
|
||||
let enable_method_str: &str = cpu_node.prop("enable-method")?;
|
||||
let enable_method = match enable_method_str {
|
||||
"psci" => CpuEnableMethod::Psci,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
Some(CpuInfo {
|
||||
id,
|
||||
compatible,
|
||||
enable_method,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
impl CpuEnableMethod {
|
||||
unsafe fn start_cpu(&self, id: usize, ip: usize, sp: usize) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Psci => {
|
||||
let psci = ARCHITECTURE.psci.try_get().ok_or_else(|| {
|
||||
warnln!(
|
||||
"cpu{} has to be enabled through PSCI, but no PSCI found",
|
||||
id
|
||||
);
|
||||
Error::InvalidArgument
|
||||
})?;
|
||||
|
||||
psci.start_cpu(id, ip, sp)
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of online CPUs, initially set to 1 (BSP processor is up)
|
||||
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
impl Psci {
|
||||
/// Function ID for CPU startup request
|
||||
const CPU_ON: u32 = 0xC4000003;
|
||||
|
||||
/// Constructs an interface instance for PSCI
|
||||
pub const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn call(&self, mut x0: u64, x1: u64, x2: u64, x3: u64) -> u64 {
|
||||
asm!("hvc #0", inout("x0") x0, in("x1") x1, in("x2") x2, in("x3") x3);
|
||||
x0
|
||||
}
|
||||
|
||||
/// Enables a single processor through a hvc/svc call.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Calling this outside of initialization sequence or more than once may lead to unexpected
|
||||
/// behavior.
|
||||
pub unsafe fn cpu_on(&self, target_cpu: usize, entry_point_address: usize, context_id: usize) {
|
||||
self.call(
|
||||
Self::CPU_ON as _,
|
||||
target_cpu as _,
|
||||
entry_point_address as _,
|
||||
context_id as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts application processors using the method specified in the device tree.
|
||||
///
|
||||
/// TODO: currently does not handle systems where APs are already started before entry.
|
||||
@ -64,68 +83,42 @@ impl Psci {
|
||||
/// The caller must ensure the physical memory manager was initialized, virtual memory tables are
|
||||
/// set up and the function has not been called before.
|
||||
pub unsafe fn start_ap_cores(dt: &DeviceTree) -> Result<(), Error> {
|
||||
let cpus = dt.node_by_path("/cpus").unwrap();
|
||||
let psci = Psci::new();
|
||||
|
||||
for cpu in cpus.children() {
|
||||
let Some(compatible) = devtree::find_prop(&cpu, "compatible") else {
|
||||
continue;
|
||||
};
|
||||
let Ok(compatible) = compatible.str() else {
|
||||
continue;
|
||||
};
|
||||
if !compatible.starts_with("arm,cortex-a") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let reg = devtree::find_prop(&cpu, "reg").unwrap().u32(0).unwrap();
|
||||
if reg == 0 {
|
||||
continue;
|
||||
extern "C" {
|
||||
fn __aarch64_ap_entry();
|
||||
}
|
||||
|
||||
for cpu in enumerate_cpus(dt).filter(|cpu| cpu.id != 0) {
|
||||
debugln!(
|
||||
"Will start {}, compatible={:?}, reg={}",
|
||||
cpu.name().unwrap(),
|
||||
compatible,
|
||||
reg
|
||||
"cpu{}: enable-method={:?}, compatible={:?}",
|
||||
cpu.id,
|
||||
cpu.enable_method,
|
||||
cpu.compatible
|
||||
);
|
||||
|
||||
const AP_STACK_PAGES: usize = 4;
|
||||
let stack_pages = phys::alloc_pages_contiguous(AP_STACK_PAGES, PageUsage::Used)?;
|
||||
debugln!(
|
||||
"{} stack: {:#x}..{:#x}",
|
||||
cpu.name().unwrap(),
|
||||
"cpu{} stack: {:#x}..{:#x}",
|
||||
cpu.id,
|
||||
stack_pages,
|
||||
stack_pages + AP_STACK_PAGES * 0x1000
|
||||
);
|
||||
// Wait for the CPU to come up
|
||||
let old_count = CPU_COUNT.load(Ordering::Acquire);
|
||||
|
||||
psci.cpu_on(
|
||||
reg as usize,
|
||||
absolute_address!(__aarch64_ap_entry).physicalize(),
|
||||
stack_pages + AP_STACK_PAGES * 0x1000,
|
||||
);
|
||||
let ip = absolute_address!(__aarch64_ap_entry).physicalize();
|
||||
let sp = stack_pages + AP_STACK_PAGES * 0x1000;
|
||||
if let Err(error) = cpu.enable_method.start_cpu(cpu.id as usize, ip, sp) {
|
||||
errorln!("Couldn't start cpu{} up: {:?}", cpu.id, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
while CPU_COUNT.load(Ordering::Acquire) == old_count {
|
||||
aarch64_cpu::asm::wfe();
|
||||
}
|
||||
|
||||
debugln!("{} is up", cpu.name().unwrap());
|
||||
debugln!("cpu{} is up", cpu.id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[naked]
|
||||
unsafe extern "C" fn __aarch64_ap_entry() -> ! {
|
||||
asm!(
|
||||
r#"
|
||||
mov sp, x0
|
||||
bl {entry} - {kernel_virt_offset}
|
||||
"#,
|
||||
entry = sym __aarch64_ap_lower_entry,
|
||||
kernel_virt_offset = const KERNEL_VIRT_OFFSET,
|
||||
options(noreturn)
|
||||
);
|
||||
}
|
||||
|
@ -362,7 +362,10 @@ impl VirtualMemoryManager for AddressSpace {
|
||||
|
||||
for page in (addr..addr + len).step_by(0x1000) {
|
||||
let Some(phys) = self.translate(page) else {
|
||||
todo!();
|
||||
todo!(
|
||||
"Tried to deallocate address not present in the table: {:#x}",
|
||||
addr
|
||||
);
|
||||
};
|
||||
|
||||
self.write_entry(page, PageEntry::INVALID, true)?;
|
||||
|
@ -4,14 +4,17 @@ use core::time::Duration;
|
||||
|
||||
use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
|
||||
use abi::error::Error;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqNumber},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
Device,
|
||||
};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::gic::IrqNumber, PLATFORM},
|
||||
device::{
|
||||
interrupt::InterruptSource, platform::Platform, timer::TimestampSource, Device,
|
||||
InitializableDevice,
|
||||
},
|
||||
arch::{Architecture, ARCHITECTURE},
|
||||
device_tree_driver,
|
||||
proc::wait,
|
||||
task::tasklet,
|
||||
};
|
||||
@ -26,23 +29,24 @@ pub struct ArmTimer {
|
||||
/// ARM timer tick interval (in some time units?)
|
||||
pub const TICK_INTERVAL: u64 = 1000000;
|
||||
|
||||
impl Device for ArmTimer {
|
||||
fn name(&self) -> &'static str {
|
||||
"ARM Generic Timer"
|
||||
impl InterruptHandler for ArmTimer {
|
||||
fn handle_irq(&self) -> bool {
|
||||
CNTP_TVAL_EL0.set(TICK_INTERVAL);
|
||||
let now = self.monotonic_timestamp().unwrap();
|
||||
|
||||
wait::tick(now);
|
||||
tasklet::tick(now);
|
||||
|
||||
unsafe {
|
||||
Cpu::local().queue().yield_cpu();
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl InitializableDevice for ArmTimer {
|
||||
type Options = ();
|
||||
|
||||
unsafe fn init(&self, _opts: ()) -> Result<(), Error> {
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TimestampSource for ArmTimer {
|
||||
fn timestamp(&self) -> Result<Duration, Error> {
|
||||
impl MonotonicTimestampProviderDevice for ArmTimer {
|
||||
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
|
||||
let count = CNTPCT_EL0.get() * 1_000_000;
|
||||
let freq = CNTFRQ_EL0.get();
|
||||
|
||||
@ -50,33 +54,61 @@ impl TimestampSource for ArmTimer {
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptSource for ArmTimer {
|
||||
fn handle_irq(&self) -> Result<bool, Error> {
|
||||
CNTP_TVAL_EL0.set(TICK_INTERVAL);
|
||||
let t = self.timestamp()?;
|
||||
|
||||
wait::tick(t);
|
||||
tasklet::tick(t);
|
||||
|
||||
unsafe {
|
||||
Cpu::local().queue().yield_cpu();
|
||||
impl Device for ArmTimer {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"ARM Generic Timer"
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
|
||||
ARCHITECTURE.register_monotonic_timer(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let intc = PLATFORM.interrupt_controller();
|
||||
let intc = ARCHITECTURE.external_interrupt_controller();
|
||||
|
||||
intc.register_irq(self.irq, Default::default(), self)?;
|
||||
|
||||
intc.register_handler(self.irq, self)?;
|
||||
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::IMASK::CLEAR);
|
||||
CNTP_TVAL_EL0.set(TICK_INTERVAL);
|
||||
|
||||
intc.enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// impl TimestampSource for ArmTimer {
|
||||
// fn timestamp(&self) -> Result<Duration, Error> {
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl InterruptSource for ArmTimer {
|
||||
// fn handle_irq(&self) -> Result<bool, Error> {
|
||||
// CNTP_TVAL_EL0.set(TICK_INTERVAL);
|
||||
// let t = self.timestamp()?;
|
||||
//
|
||||
// wait::tick(t);
|
||||
// tasklet::tick(t);
|
||||
//
|
||||
// unsafe {
|
||||
// Cpu::local().queue().yield_cpu();
|
||||
// }
|
||||
//
|
||||
// Ok(true)
|
||||
// }
|
||||
//
|
||||
// unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
// let intc = PLATFORM.interrupt_controller();
|
||||
//
|
||||
// intc.register_handler(self.irq, self)?;
|
||||
// intc.enable_irq(self.irq)?;
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ArmTimer {
|
||||
/// Constructs an instance of ARM generic timer.
|
||||
///
|
||||
@ -87,3 +119,11 @@ impl ArmTimer {
|
||||
Self { irq }
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["arm,armv8-timer"],
|
||||
probe(_dt) => {
|
||||
// TODO actually get info from the dt
|
||||
Some(Box::new(unsafe { ArmTimer::new(IrqNumber::Private(14)) }))
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Provides architecture/platform-specific implementation details
|
||||
|
||||
use abi::error::Error;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
/// Returns an absolute address to the given symbol
|
||||
#[macro_export]
|
||||
@ -21,23 +20,29 @@ macro_rules! absolute_address {
|
||||
}
|
||||
|
||||
pub mod aarch64;
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "aarch64")] {
|
||||
|
||||
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE, PlatformImpl, PLATFORM};
|
||||
} else if #[cfg(target_arch = "x86_64")] {
|
||||
pub mod x86_64;
|
||||
|
||||
pub use x86_64::{
|
||||
X86_64 as ArchitectureImpl,
|
||||
X86_64 as PlatformImpl,
|
||||
ARCHITECTURE,
|
||||
ARCHITECTURE as PLATFORM
|
||||
};
|
||||
} else {
|
||||
compile_error!("Architecture is not supported");
|
||||
}
|
||||
}
|
||||
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
|
||||
use device_api::{
|
||||
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
};
|
||||
// cfg_if! {
|
||||
// if #[cfg(target_arch = "aarch64")] {
|
||||
//
|
||||
// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE, PlatformImpl, PLATFORM};
|
||||
// } else if #[cfg(target_arch = "x86_64")] {
|
||||
// pub mod x86_64;
|
||||
//
|
||||
// pub use x86_64::{
|
||||
// X86_64 as ArchitectureImpl,
|
||||
// X86_64 as PlatformImpl,
|
||||
// ARCHITECTURE,
|
||||
// ARCHITECTURE as PLATFORM
|
||||
// };
|
||||
// } else {
|
||||
// compile_error!("Architecture is not supported");
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Describes messages sent from some CPU to others
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
@ -82,4 +87,54 @@ pub trait Architecture {
|
||||
|
||||
/// Returns the count of present CPUs, including the BSP
|
||||
fn cpu_count() -> usize;
|
||||
|
||||
/// Adds an external interrupt controller to the system
|
||||
fn register_external_interrupt_controller(
|
||||
&self,
|
||||
intc: &'static dyn ExternalInterruptController,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Adds a local interrupt controller to the system
|
||||
fn register_local_interrupt_controller(
|
||||
&self,
|
||||
intc: &'static dyn LocalInterruptController<IpiMessage = CpuMessage>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Adds a monotonic timer to the system
|
||||
fn register_monotonic_timer(
|
||||
&self,
|
||||
timer: &'static dyn MonotonicTimestampProviderDevice,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
// TODO only supports 1 extintc per system
|
||||
/// Returns the primary external interrupt controller
|
||||
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController;
|
||||
|
||||
/// Returns the local interrupt controller
|
||||
fn local_interrupt_controller(
|
||||
&self,
|
||||
) -> &'static dyn LocalInterruptController<IpiMessage = CpuMessage>;
|
||||
|
||||
/// Returns the monotonic timer
|
||||
fn monotonic_timer(&self) -> &'static dyn MonotonicTimestampProviderDevice;
|
||||
|
||||
/// Sends a message to the requested set of CPUs through an interprocessor interrupt.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// u64 limits the number of targetable CPUs to (only) 64. Platform-specific implementations
|
||||
/// may impose narrower restrictions.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// As the call may alter the flow of execution on CPUs, this function is unsafe.
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error>;
|
||||
|
||||
/// Performs a CPU reset.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure it is actually safe to reset, i.e. no critical processes will be
|
||||
/// aborted and no data will be lost.
|
||||
unsafe fn reset(&self) -> !;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||
task,
|
||||
};
|
||||
|
||||
use super::{smp::CPU_COUNT, ARCHITECTURE};
|
||||
use super::ARCHITECTURE;
|
||||
|
||||
const BOOT_STACK_SIZE: usize = 65536;
|
||||
|
||||
@ -81,39 +81,40 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
|
||||
|
||||
/// Application processor entry point
|
||||
pub extern "C" fn __x86_64_ap_entry() -> ! {
|
||||
let cpu_id = CPU_COUNT.load(Ordering::Acquire);
|
||||
loop {}
|
||||
// let cpu_id = CPU_COUNT.load(Ordering::Acquire);
|
||||
|
||||
MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
unsafe {
|
||||
core::arch::asm!("swapgs");
|
||||
}
|
||||
MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
unsafe {
|
||||
core::arch::asm!("swapgs");
|
||||
}
|
||||
// MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
// unsafe {
|
||||
// core::arch::asm!("swapgs");
|
||||
// }
|
||||
// MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
// unsafe {
|
||||
// core::arch::asm!("swapgs");
|
||||
// }
|
||||
|
||||
// Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base
|
||||
cpuid::feature_gate();
|
||||
// // Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base
|
||||
// cpuid::feature_gate();
|
||||
|
||||
infoln!("cpu{} initializing", cpu_id);
|
||||
unsafe {
|
||||
ARCHITECTURE.init_mmu(false);
|
||||
core::arch::asm!("wbinvd");
|
||||
// infoln!("cpu{} initializing", cpu_id);
|
||||
// unsafe {
|
||||
// ARCHITECTURE.init_mmu(false);
|
||||
// core::arch::asm!("wbinvd");
|
||||
|
||||
Cpu::init_local(LocalApic::new(), cpu_id as u32);
|
||||
syscall::init_syscall();
|
||||
exception::init_exceptions(cpu_id);
|
||||
// Cpu::init_local(LocalApic::new(), cpu_id as u32);
|
||||
// syscall::init_syscall();
|
||||
// exception::init_exceptions(cpu_id);
|
||||
|
||||
ARCHITECTURE.init(false).unwrap();
|
||||
}
|
||||
// ARCHITECTURE.init(false).unwrap();
|
||||
// }
|
||||
|
||||
CPU_COUNT.fetch_add(1, Ordering::Release);
|
||||
// CPU_COUNT.fetch_add(1, Ordering::Release);
|
||||
|
||||
CPU_INIT_FENCE.wait_one();
|
||||
// CPU_INIT_FENCE.wait_one();
|
||||
|
||||
infoln!("cpu{} initialized", cpu_id);
|
||||
// infoln!("cpu{} initialized", cpu_id);
|
||||
|
||||
unsafe { task::enter() }
|
||||
// unsafe { task::enter() }
|
||||
}
|
||||
|
||||
global_asm!(
|
||||
|
3
src/device/bus/mod.rs
Normal file
3
src/device/bus/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Bus devices
|
||||
|
||||
pub mod simple_bus;
|
27
src/device/bus/simple_bus.rs
Normal file
27
src/device/bus/simple_bus.rs
Normal file
@ -0,0 +1,27 @@
|
||||
//! Simple "passthrough" bus device
|
||||
|
||||
use crate::{arch::aarch64::devtree::DevTreeIndexNodeExt, device, device_tree_driver};
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["simple-bus"],
|
||||
probe(dt) => {
|
||||
let address_cells = dt.node.address_cells();
|
||||
let size_cells = dt.node.size_cells();
|
||||
|
||||
let nodes = dt.node.children();
|
||||
|
||||
// Iterate devices on the bus
|
||||
device::enumerate_dt(address_cells, size_cells, nodes, |_, probe| {
|
||||
if let Some((device, _)) = device::probe_dt_node(&probe) {
|
||||
unsafe {
|
||||
device.init()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}).ok();
|
||||
|
||||
// Don't yield any devices
|
||||
None
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
//! Interrupt-related interfaces
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use abi::error::Error;
|
||||
|
||||
use super::Device;
|
||||
|
||||
/// Convenience type alias for a static IRQ handler
|
||||
pub type IrqHandler = &'static (dyn InterruptSource + Sync);
|
||||
|
||||
/// Specifies the target(s) of interprocessor interrupt delivery
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum IpiDeliveryTarget {
|
||||
/// IPI will be delivered to every CPU except the local one
|
||||
AllExceptLocal,
|
||||
/// IPI will only be sent to CPUs specified in the mask
|
||||
Specified(u64),
|
||||
}
|
||||
|
||||
/// Interface for a device capable of emitting interrupts
|
||||
pub trait InterruptSource: Device {
|
||||
/// Initializes and enables IRQs for the device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the function hasn't been called before.
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error>;
|
||||
|
||||
/// Handles the interrupt raised by the device. Returns `true` if the interrupt was handled,
|
||||
/// `false` if device itself sent no interrupt (may be possible if several interrupt handlers
|
||||
/// share the same IRQ vector).
|
||||
fn handle_irq(&self) -> Result<bool, Error>;
|
||||
}
|
||||
|
||||
// TODO not yet ready for this
|
||||
// /// Controls per-CPU interrupt delivery
|
||||
// pub trait LocalInterruptController {
|
||||
// type Vector;
|
||||
// type Id;
|
||||
//
|
||||
// /// Allocates a vector for external IRQ
|
||||
// unsafe fn allocate_irq_vector(&self) -> Result<Self::Vector, Error>;
|
||||
//
|
||||
// /// Returns the unique ID of this local interrupt controller
|
||||
// fn id(&self) -> Self::Id;
|
||||
//
|
||||
// }
|
||||
|
||||
/// Interface for a device responsible for routing and handling IRQs from external sources
|
||||
pub trait ExternalInterruptController: Device {
|
||||
/// Interrupt number wrapper type
|
||||
type IrqNumber;
|
||||
|
||||
/// Binds an interrupt number to its handler implementation
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn InterruptSource + Sync),
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Configures how an interrupt should be handled by the controller
|
||||
fn configure_irq(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
active_low: bool,
|
||||
level_triggered: bool,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Enables given interrupt number/vector
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Token type to indicate that the code is being run from an interrupt handler
|
||||
pub struct IrqContext<'irq> {
|
||||
_0: PhantomData<&'irq ()>,
|
||||
}
|
||||
|
||||
impl<'irq> IrqContext<'irq> {
|
||||
/// Constructs an IRQ context token
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only allowed to be constructed in top-level IRQ handlers
|
||||
#[inline(always)]
|
||||
pub const unsafe fn new() -> Self {
|
||||
Self { _0: PhantomData }
|
||||
}
|
||||
}
|
@ -1,31 +1,185 @@
|
||||
//! Device management and interfaces
|
||||
|
||||
use abi::error::Error;
|
||||
use core::mem::size_of;
|
||||
|
||||
#[cfg(feature = "fb_console")]
|
||||
pub mod display;
|
||||
pub mod input;
|
||||
pub mod interrupt;
|
||||
pub mod platform;
|
||||
use abi::error::Error;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{manager::DeviceManager, Device, DeviceId};
|
||||
use fdt_rs::{index::DevTreeIndexNode, prelude::PropReader};
|
||||
|
||||
use crate::{
|
||||
arch::aarch64::devtree,
|
||||
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
|
||||
};
|
||||
|
||||
pub mod bus;
|
||||
pub mod power;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
pub mod tty;
|
||||
|
||||
/// General device interface
|
||||
pub trait Device {
|
||||
/// Returns a display name for the device
|
||||
fn name(&self) -> &'static str;
|
||||
// pub mod display;
|
||||
|
||||
static DEVICE_MANAGER: IrqSafeSpinlock<DeviceManager> = IrqSafeSpinlock::new(DeviceManager::new());
|
||||
|
||||
/// Helper macro to return the count of expressions supplied to it
|
||||
#[macro_export]
|
||||
macro_rules! count {
|
||||
() => (0usize);
|
||||
($x:tt $($xs:tt)*) => (1usize + $crate::count!($($xs)*));
|
||||
}
|
||||
|
||||
/// Interface for device initialization
|
||||
pub trait InitializableDevice {
|
||||
/// Options provided when initializing the device
|
||||
type Options;
|
||||
/// Registers a device driver for compatible device tree nodes
|
||||
///
|
||||
/// # Usage example
|
||||
///
|
||||
/// ```
|
||||
/// device_tree_driver! {
|
||||
/// compatible: ["arm,pl011"],
|
||||
/// probe(of) => {
|
||||
/// let my_device = ...; // ... extract some info about the device ...
|
||||
/// Some(Box::new(my_device))
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! device_tree_driver {
|
||||
(
|
||||
compatible: [$($compatible:literal),+],
|
||||
probe ($node:ident) => $probe_body:block $(,)?
|
||||
) => {
|
||||
const __COMPATIBLE_LEN: usize = $crate::count!($($compatible )+);
|
||||
static __COMPATIBLE: [&str; __COMPATIBLE_LEN] = [$($compatible),+];
|
||||
|
||||
/// Initializes the device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: only meant to be called once for each device.
|
||||
unsafe fn init(&self, opts: Self::Options) -> Result<(), Error>;
|
||||
fn __probe($node: &$crate::device::DevTreeNodeInfo) ->
|
||||
Option<alloc::boxed::Box<dyn device_api::Device>> $probe_body
|
||||
|
||||
core::arch::global_asm!(r#"
|
||||
.pushsection .dt_probes, "a"
|
||||
.quad {compatible}
|
||||
.quad {compatible_len}
|
||||
.quad {probe_func}
|
||||
.popsection
|
||||
"#,
|
||||
compatible = sym __COMPATIBLE,
|
||||
compatible_len = const __COMPATIBLE_LEN,
|
||||
probe_func = sym __probe
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
struct DevTreeProbe<'a> {
|
||||
compatible: &'static [&'static str],
|
||||
probe_func: fn(&'a DevTreeNodeInfo<'a, 'a, 'a>) -> Option<Box<dyn Device>>,
|
||||
}
|
||||
|
||||
/// Provides information about a device tree node to device driver's "probe" function
|
||||
pub struct DevTreeNodeInfo<'a, 'i, 'dt> {
|
||||
/// #address-cells property of the parent bus/system
|
||||
pub address_cells: usize,
|
||||
/// #size-cells property of the parent bus/system
|
||||
pub size_cells: usize,
|
||||
/// Device tree node being probed
|
||||
pub node: DevTreeIndexNode<'a, 'i, 'dt>,
|
||||
}
|
||||
|
||||
fn iter_dt_probes<'a>() -> impl Iterator<Item = DevTreeProbe<'a>> {
|
||||
extern "C" {
|
||||
static __dt_probes_start: u64;
|
||||
static __dt_probes_end: u64;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let base = &__dt_probes_start as *const u64;
|
||||
let end = &__dt_probes_end as *const u64;
|
||||
let len = (end as usize - base as usize) / (size_of::<u64>() * 3);
|
||||
|
||||
(0..len).map(move |i| {
|
||||
let compatible_ptr = *base.add(i * 3);
|
||||
let compatible_len = *base.add(i * 3 + 1);
|
||||
let probe_func_ptr = *base.add(i * 3 + 2);
|
||||
|
||||
let compatible =
|
||||
core::slice::from_raw_parts(compatible_ptr as *const &str, compatible_len as usize);
|
||||
let probe_func = core::mem::transmute(probe_func_ptr);
|
||||
|
||||
DevTreeProbe {
|
||||
compatible,
|
||||
probe_func,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn dt_match_compatible(compatible: &str) -> Option<DevTreeProbe> {
|
||||
iter_dt_probes().find(|probe| probe.compatible.contains(&compatible))
|
||||
}
|
||||
|
||||
/// "Probes" a device tree node for any matching device, registering it if a compatible driver is
|
||||
/// found
|
||||
pub fn probe_dt_node(dt: &DevTreeNodeInfo) -> Option<(&'static dyn Device, DeviceId)> {
|
||||
// TODO use list, not just the first item
|
||||
let Some(compatible) =
|
||||
devtree::find_prop(&dt.node, "compatible").and_then(|prop| prop.str().ok())
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let probe = dt_match_compatible(compatible)?;
|
||||
let device = Box::leak((probe.probe_func)(dt)?);
|
||||
let id = register_device(device);
|
||||
Some((device, id))
|
||||
}
|
||||
|
||||
/// Performs shallow walk of a device tree node and executes the visitor function on each node
|
||||
pub fn enumerate_dt<
|
||||
'a,
|
||||
I: Iterator<Item = DevTreeIndexNode<'a, 'a, 'a>>,
|
||||
F: Fn(&str, DevTreeNodeInfo) -> Result<(), Error>,
|
||||
>(
|
||||
address_cells: usize,
|
||||
size_cells: usize,
|
||||
nodes: I,
|
||||
f: F,
|
||||
) -> Result<(), usize> {
|
||||
let mut failed_count = 0;
|
||||
|
||||
for node in nodes {
|
||||
// Skip /cpus and /memory*
|
||||
let probe = DevTreeNodeInfo {
|
||||
address_cells,
|
||||
size_cells,
|
||||
node,
|
||||
};
|
||||
|
||||
let Ok(name) = probe.node.name() else {
|
||||
continue;
|
||||
};
|
||||
let Some(compatible) =
|
||||
devtree::find_prop(&probe.node, "compatible").and_then(|prop| prop.str().ok())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Err(error) = f(compatible, probe) {
|
||||
warnln!("{}: {:?}", name, error);
|
||||
failed_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if failed_count == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(failed_count)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a device to the kernel's device table and returns the ID assigned to it
|
||||
pub fn register_device(device: &'static dyn Device) -> DeviceId {
|
||||
debugln!("Register {:?}", device.display_name());
|
||||
DEVICE_MANAGER.lock().register(device)
|
||||
}
|
||||
|
||||
/// Returns a safe reference to the kernel's [DeviceManager] instance
|
||||
pub fn manager_lock<'a>() -> IrqSafeSpinlockGuard<'a, DeviceManager> {
|
||||
DEVICE_MANAGER.lock()
|
||||
}
|
||||
|
@ -11,61 +11,10 @@ use super::{
|
||||
|
||||
/// Platform interface for interacting with a general hardware set
|
||||
pub trait Platform {
|
||||
/// Interrupt number type for the platform
|
||||
type IrqNumber;
|
||||
|
||||
/// Address, to which the kernel is expected to be loaded for this platform
|
||||
const KERNEL_PHYS_BASE: usize;
|
||||
|
||||
/// Initializes the platform devices to their usable state.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe to call if the platform has already been initialized.
|
||||
unsafe fn init(&'static self, is_bsp: bool) -> Result<(), Error>;
|
||||
|
||||
/// Initializes the debug devices to provide early debug output.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe to call if the device has already been initialized.
|
||||
unsafe fn init_debug(&'static self);
|
||||
|
||||
/// Returns a display name for the platform
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Returns a reference to the platform's interrupt controller.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// May not be initialized at the moment of calling.
|
||||
fn interrupt_controller(&self)
|
||||
-> &dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>;
|
||||
|
||||
/// Returns the platform's primary timestamp source.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// May not be initialized at the moment of calling.
|
||||
fn timestamp_source(&self) -> &dyn TimestampSource;
|
||||
|
||||
/// Sends a message to the requested set of CPUs through an interprocessor interrupt.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// u64 limits the number of targetable CPUs to (only) 64. Platform-specific implementations
|
||||
/// may impose narrower restrictions.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// As the call may alter the flow of execution on CPUs, this function is unsafe.
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error>;
|
||||
|
||||
/// Performs a CPU reset.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure it is actually safe to reset, i.e. no critical processes will be
|
||||
/// aborted and no data will be lost.
|
||||
unsafe fn reset(&self) -> !;
|
||||
}
|
||||
|
81
src/device/power/arm_psci.rs
Normal file
81
src/device/power/arm_psci.rs
Normal file
@ -0,0 +1,81 @@
|
||||
//! ARM PSCI driver implementation
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{CpuBringupDevice, Device};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::devtree, ARCHITECTURE},
|
||||
device_tree_driver,
|
||||
};
|
||||
|
||||
enum CallMethod {
|
||||
Hvc,
|
||||
Smc,
|
||||
}
|
||||
|
||||
/// ARM Power State Coordination Interface driver
|
||||
pub struct Psci {
|
||||
method: CallMethod,
|
||||
cpu_on: u32,
|
||||
#[allow(dead_code)]
|
||||
cpu_off: u32,
|
||||
#[allow(dead_code)]
|
||||
cpu_suspend: u32,
|
||||
}
|
||||
|
||||
impl Device for Psci {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"ARM PSCI"
|
||||
}
|
||||
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
ARCHITECTURE.psci.init(self);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuBringupDevice for Psci {
|
||||
unsafe fn start_cpu(&self, id: usize, ip: usize, arg0: usize) -> Result<(), Error> {
|
||||
self.call(self.cpu_on as _, id as _, ip as _, arg0 as _);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Psci {
|
||||
#[inline]
|
||||
unsafe fn call(&self, mut x0: u64, x1: u64, x2: u64, x3: u64) -> u64 {
|
||||
match self.method {
|
||||
CallMethod::Hvc => {
|
||||
core::arch::asm!("hvc #0", inlateout("x0") x0, in("x1") x1, in("x2") x2, in("x3") x3)
|
||||
}
|
||||
CallMethod::Smc => {
|
||||
core::arch::asm!("smc #0", inlateout("x0") x0, in("x1") x1, in("x2") x2, in("x3") x3)
|
||||
}
|
||||
}
|
||||
x0
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["arm,psci-1.0", "arm,psci"],
|
||||
probe(dt) => {
|
||||
let method = devtree::find_prop(&dt.node, "method").and_then(|prop| prop.str().ok())?;
|
||||
let method = match method {
|
||||
"hvc" => CallMethod::Hvc,
|
||||
"smc" => CallMethod::Smc,
|
||||
_ => panic!("Unknown PSCI call method: {:?}", method)
|
||||
};
|
||||
let cpu_on = devtree::find_prop(&dt.node, "cpu_on").and_then(|prop| prop.u32(0).ok())?;
|
||||
let cpu_off = devtree::find_prop(&dt.node, "cpu_off").and_then(|prop| prop.u32(0).ok())?;
|
||||
let cpu_suspend = devtree::find_prop(&dt.node, "cpu_suspend").and_then(|prop| prop.u32(0).ok())?;
|
||||
|
||||
Some(Box::new(Psci {
|
||||
method,
|
||||
cpu_on,
|
||||
cpu_off,
|
||||
cpu_suspend
|
||||
}))
|
||||
}
|
||||
}
|
3
src/device/power/mod.rs
Normal file
3
src/device/power/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Power-management related device drivers
|
||||
|
||||
pub mod arm_psci;
|
@ -1,16 +1,4 @@
|
||||
//! Serial device interfaces
|
||||
use abi::error::Error;
|
||||
|
||||
use super::Device;
|
||||
|
||||
#[cfg(all(target_arch = "aarch64", not(feature = "aarch64_orange_pi3")))]
|
||||
pub mod pl011;
|
||||
|
||||
/// Generic serial device interface
|
||||
pub trait SerialDevice: Device {
|
||||
/// Sends (blocking) a single byte into the serial port
|
||||
fn send(&self, byte: u8) -> Result<(), Error>;
|
||||
|
||||
/// Receive a single byte from the serial port, blocking if necessary
|
||||
fn receive(&self, blocking: bool) -> Result<u8, Error>;
|
||||
}
|
||||
pub mod sunxi_uart;
|
||||
|
@ -1,5 +1,11 @@
|
||||
//! ARM PL011 driver
|
||||
use abi::{error::Error, io::DeviceRequest};
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqNumber},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
@ -7,16 +13,15 @@ use tock_registers::{
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
|
||||
use super::SerialDevice;
|
||||
use crate::{
|
||||
arch::{aarch64::gic::IrqNumber, PLATFORM},
|
||||
debug::DebugSink,
|
||||
device::{
|
||||
interrupt::InterruptSource,
|
||||
platform::Platform,
|
||||
tty::{CharRing, TtyDevice},
|
||||
Device, InitializableDevice,
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexPropExt},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
debug::{self, DebugSink, LogLevel},
|
||||
device::tty::{CharRing, TtyDevice},
|
||||
device_tree_driver,
|
||||
fs::devfs::{self, CharDeviceType},
|
||||
mem::device::DeviceMemoryIo,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
@ -81,19 +86,6 @@ impl Pl011Inner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv_byte(&mut self, blocking: bool) -> Result<u8, Error> {
|
||||
if self.regs.FR.matches_all(FR::RXFE::SET) {
|
||||
if !blocking {
|
||||
todo!();
|
||||
}
|
||||
while self.regs.FR.matches_all(FR::RXFE::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.regs.DR.get() as u8)
|
||||
}
|
||||
|
||||
unsafe fn init(&mut self) {
|
||||
self.regs.CR.set(0);
|
||||
self.regs.ICR.write(ICR::ALL::CLEAR);
|
||||
@ -141,68 +133,82 @@ impl SerialDevice for Pl011 {
|
||||
fn send(&self, byte: u8) -> Result<(), Error> {
|
||||
self.inner.get().lock().send_byte(byte)
|
||||
}
|
||||
|
||||
fn receive(&self, blocking: bool) -> Result<u8, Error> {
|
||||
self.inner.get().lock().recv_byte(blocking)
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptSource for Pl011 {
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let intc = PLATFORM.interrupt_controller();
|
||||
|
||||
intc.register_handler(self.irq, self)?;
|
||||
self.inner.get().lock().regs.IMSC.modify(IMSC::RXIM::SET);
|
||||
intc.enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_irq(&self) -> Result<bool, Error> {
|
||||
impl InterruptHandler for Pl011 {
|
||||
fn handle_irq(&self) -> bool {
|
||||
let inner = self.inner.get().lock();
|
||||
inner.regs.ICR.write(ICR::ALL::CLEAR);
|
||||
|
||||
let byte = inner.regs.DR.get();
|
||||
drop(inner);
|
||||
|
||||
self.recv_byte(byte as u8);
|
||||
if byte == b'\x1b' as u32 {
|
||||
use crate::task::sched::CpuQueue;
|
||||
|
||||
Ok(true)
|
||||
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 InitializableDevice for Pl011 {
|
||||
type Options = ();
|
||||
impl Device for Pl011 {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"Primecell PL011 UART"
|
||||
}
|
||||
|
||||
unsafe fn init(&self, _opts: ()) -> Result<(), Error> {
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
let mut inner = Pl011Inner {
|
||||
regs: DeviceMemoryIo::map("pl011 UART", self.base)?,
|
||||
};
|
||||
inner.init();
|
||||
|
||||
self.inner.init(IrqSafeSpinlock::new(inner));
|
||||
|
||||
debug::add_sink(self, LogLevel::Debug);
|
||||
devfs::add_char_device(self, CharDeviceType::TtySerial)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let intc = ARCHITECTURE.external_interrupt_controller();
|
||||
|
||||
intc.register_irq(self.irq, Default::default(), self)?;
|
||||
self.inner.get().lock().regs.IMSC.modify(IMSC::RXIM::SET);
|
||||
intc.enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pl011 {
|
||||
fn name(&self) -> &'static str {
|
||||
"pl011"
|
||||
}
|
||||
}
|
||||
device_tree_driver! {
|
||||
compatible: ["arm,pl011"],
|
||||
probe(of) => {
|
||||
let reg = devtree::find_prop(&of.node, "reg")?;
|
||||
let (base, _) = reg.cell2_array_item(0, of.address_cells, of.size_cells)?;
|
||||
|
||||
impl Pl011 {
|
||||
/// Constructs an instance of the device at `base`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the address is valid.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
Some(Box::new(Pl011 {
|
||||
inner: OneTimeInit::new(),
|
||||
// TODO obtain IRQ from dt
|
||||
irq: IrqNumber::Shared(1),
|
||||
ring: CharRing::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
base: base as usize
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
154
src/device/serial/sunxi_uart.rs
Normal file
154
src/device/serial/sunxi_uart.rs
Normal file
@ -0,0 +1,154 @@
|
||||
//! Allwinner (H6) UART implementation
|
||||
|
||||
use abi::{error::Error, io::DeviceRequest};
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqNumber},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexPropExt},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
debug::{self, DebugSink, LogLevel},
|
||||
device::tty::{CharRing, TtyDevice},
|
||||
device_tree_driver,
|
||||
fs::devfs::{self, CharDeviceType},
|
||||
mem::device::DeviceMemoryIo,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
USR [
|
||||
TFE OFFSET(2) NUMBITS(1) [],
|
||||
TFNF OFFSET(1) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => DLL: ReadWrite<u32>),
|
||||
(0x04 => _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)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
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 = ARCHITECTURE.external_interrupt_controller();
|
||||
intc.register_irq(self.irq, Default::default(), self)?;
|
||||
intc.enable_irq(self.irq)?;
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ use abi::{
|
||||
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
|
||||
process::Signal,
|
||||
};
|
||||
use device_api::serial::SerialDevice;
|
||||
|
||||
use crate::{
|
||||
proc::wait::Wait,
|
||||
@ -11,8 +12,6 @@ use crate::{
|
||||
task::{process::Process, ProcessId},
|
||||
};
|
||||
|
||||
use super::serial::SerialDevice;
|
||||
|
||||
#[cfg(feature = "fb_console")]
|
||||
pub mod combined {
|
||||
use abi::{error::Error, io::DeviceRequest};
|
||||
|
@ -56,6 +56,8 @@ fn setup_root() -> Result<VnodeRef, Error> {
|
||||
/// This function is meant to be used as a kernel-space process after all the platform-specific
|
||||
/// initialization has finished.
|
||||
pub fn kernel_main() {
|
||||
infoln!("In main");
|
||||
|
||||
#[cfg(feature = "fb_console")]
|
||||
{
|
||||
use core::time::Duration;
|
||||
|
@ -36,6 +36,8 @@ impl DeviceMemory {
|
||||
let base = ARCHITECTURE.map_device_pages(aligned_base, aligned_size / 0x1000)?;
|
||||
let base = base + base_offset;
|
||||
|
||||
debugln!("Mapped {}@{:#x} to {:#x}", name, phys, base);
|
||||
|
||||
Ok(Self { name, base, size })
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Kernel's global heap allocator
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
ops::Range,
|
||||
ptr::{null_mut, NonNull},
|
||||
};
|
||||
|
||||
@ -21,13 +22,21 @@ impl KernelAllocator {
|
||||
unsafe fn init(&self, base: usize, size: usize) {
|
||||
self.inner.lock().init(base as _, size);
|
||||
}
|
||||
|
||||
fn range(&self) -> Range<usize> {
|
||||
let lock = self.inner.lock();
|
||||
lock.bottom() as usize..lock.top() as usize
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GlobalAlloc for KernelAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
match self.inner.lock().allocate_first_fit(layout) {
|
||||
Ok(v) => v.as_ptr(),
|
||||
Err(_) => null_mut(),
|
||||
Err(e) => {
|
||||
errorln!("Failed to allocate {:?}: {:?}", layout, e);
|
||||
null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +55,10 @@ static GLOBAL_HEAP: KernelAllocator = KernelAllocator::empty();
|
||||
///
|
||||
/// The caller must ensure the range is valid and mapped virtual memory.
|
||||
pub unsafe fn init_heap(heap_base: usize, heap_size: usize) {
|
||||
debugln!("Heap: {:#x}..{:#x}", heap_base, heap_base + heap_size);
|
||||
GLOBAL_HEAP.init(heap_base, heap_size);
|
||||
}
|
||||
|
||||
/// Returns the heap address range
|
||||
pub fn heap_range() -> Range<usize> {
|
||||
GLOBAL_HEAP.range()
|
||||
}
|
||||
|
@ -7,10 +7,7 @@ use abi::error::Error;
|
||||
|
||||
// use abi::error::Error;
|
||||
//
|
||||
use crate::{
|
||||
arch::{Architecture, ArchitectureImpl, PlatformImpl},
|
||||
device::platform::Platform,
|
||||
};
|
||||
use crate::arch::{Architecture, ArchitectureImpl /*, PlatformImpl*/};
|
||||
|
||||
use self::table::AddressSpace;
|
||||
//
|
||||
@ -22,7 +19,7 @@ pub mod phys;
|
||||
pub mod table;
|
||||
|
||||
/// Kernel's physical load address
|
||||
pub const KERNEL_PHYS_BASE: usize = PlatformImpl::KERNEL_PHYS_BASE;
|
||||
// pub const KERNEL_PHYS_BASE: usize = PlatformImpl::KERNEL_PHYS_BASE;
|
||||
/// Kernel's virtual memory mapping offset (i.e. kernel's virtual address is [KERNEL_PHYS_BASE] +
|
||||
/// [KERNEL_VIRT_OFFSET])
|
||||
pub const KERNEL_VIRT_OFFSET: usize = ArchitectureImpl::KERNEL_VIRT_OFFSET;
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
debug::LogLevel,
|
||||
mem::{
|
||||
phys::reserved::{is_reserved, reserve_region},
|
||||
ConvertAddress, KERNEL_PHYS_BASE,
|
||||
ConvertAddress, /*, KERNEL_PHYS_BASE */
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
@ -258,12 +258,12 @@ pub unsafe fn init_from_iter<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
|
||||
|
||||
fn kernel_physical_memory_region() -> PhysicalMemoryRegion {
|
||||
extern "C" {
|
||||
static __kernel_phys_start: u8;
|
||||
static __kernel_size: u8;
|
||||
}
|
||||
|
||||
let base = absolute_address!(__kernel_phys_start);
|
||||
let size = absolute_address!(__kernel_size);
|
||||
|
||||
PhysicalMemoryRegion {
|
||||
base: KERNEL_PHYS_BASE,
|
||||
size,
|
||||
}
|
||||
PhysicalMemoryRegion { base, size }
|
||||
}
|
||||
|
28
src/panic.rs
28
src/panic.rs
@ -1,10 +1,12 @@
|
||||
//! Kernel panic handler code
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use device_api::interrupt::IpiDeliveryTarget;
|
||||
|
||||
use crate::{
|
||||
arch::{Architecture, ArchitectureImpl},
|
||||
arch::{Architecture, ArchitectureImpl, CpuMessage, ARCHITECTURE},
|
||||
debug::{debug_internal, LogLevel},
|
||||
sync::SpinFence,
|
||||
sync::{hack_locks, SpinFence},
|
||||
task::{sched::CpuQueue, Cpu},
|
||||
};
|
||||
|
||||
@ -41,19 +43,19 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
.compare_exchange(false, true, Ordering::Release, Ordering::Acquire)
|
||||
.is_ok()
|
||||
{
|
||||
// // Let other CPUs know we're screwed
|
||||
// unsafe {
|
||||
// PLATFORM
|
||||
// .send_ipi(IpiDeliveryTarget::AllExceptLocal, CpuMessage::Panic)
|
||||
// .ok();
|
||||
// }
|
||||
// Let other CPUs know we're screwed
|
||||
unsafe {
|
||||
ARCHITECTURE
|
||||
.send_ipi(IpiDeliveryTarget::OtherCpus, CpuMessage::Panic)
|
||||
.ok();
|
||||
}
|
||||
|
||||
// let ap_count = ArchitectureImpl::cpu_count() - 1;
|
||||
// PANIC_HANDLED_FENCE.wait_all(ap_count);
|
||||
let ap_count = ArchitectureImpl::cpu_count() - 1;
|
||||
PANIC_HANDLED_FENCE.wait_all(ap_count);
|
||||
|
||||
// unsafe {
|
||||
// hack_locks();
|
||||
// }
|
||||
unsafe {
|
||||
hack_locks();
|
||||
}
|
||||
|
||||
log_print_raw!(LogLevel::Fatal, "--- BEGIN PANIC ---\n");
|
||||
log_print_raw!(LogLevel::Fatal, "In CPU {}\n", Cpu::local_id());
|
||||
|
@ -5,8 +5,7 @@ use abi::error::Error;
|
||||
use alloc::{collections::LinkedList, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
arch::PLATFORM,
|
||||
device::platform::Platform,
|
||||
arch::{Architecture, ARCHITECTURE},
|
||||
sync::IrqSafeSpinlock,
|
||||
task::process::{Process, ProcessState},
|
||||
};
|
||||
@ -124,7 +123,7 @@ impl Wait {
|
||||
queue_lock = self.queue.lock();
|
||||
|
||||
if let Some(deadline) = deadline {
|
||||
let now = PLATFORM.timestamp_source().timestamp()?;
|
||||
let now = ARCHITECTURE.monotonic_timer().monotonic_timestamp()?;
|
||||
|
||||
if now > deadline {
|
||||
let mut cursor = queue_lock.cursor_front_mut();
|
||||
@ -150,7 +149,7 @@ static TICK_LIST: IrqSafeSpinlock<LinkedList<Timeout>> = IrqSafeSpinlock::new(Li
|
||||
/// Suspends current task until given deadline
|
||||
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Error> {
|
||||
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
|
||||
let now = PLATFORM.timestamp_source().timestamp()?;
|
||||
let now = ARCHITECTURE.monotonic_timer().monotonic_timestamp()?;
|
||||
let deadline = now + timeout;
|
||||
|
||||
match SLEEP_NOTIFY.wait(Some(deadline)) {
|
||||
|
@ -79,7 +79,7 @@ pub fn init() -> Result<(), Error> {
|
||||
let cpu_count = ArchitectureImpl::cpu_count();
|
||||
|
||||
// Create a queue for each CPU
|
||||
sched::init_queues(Vec::from_iter((0..cpu_count).map(|_| CpuQueue::new())));
|
||||
sched::init_queues(Vec::from_iter((0..cpu_count).map(CpuQueue::new)));
|
||||
|
||||
spawn_kernel_closure("[kmain]", kernel_main)?;
|
||||
// spawn_kernel_closure(move || loop {
|
||||
|
@ -59,6 +59,8 @@ struct ProcessInner {
|
||||
session_terminal: Option<VnodeRef>,
|
||||
signal_entry: Option<SignalEntry>,
|
||||
signal_stack: VecDeque<Signal>,
|
||||
|
||||
queue: Option<&'static CpuQueue>,
|
||||
}
|
||||
|
||||
/// Process data and state structure
|
||||
@ -110,6 +112,8 @@ impl Process {
|
||||
session_terminal: None,
|
||||
signal_entry: None,
|
||||
signal_stack: VecDeque::new(),
|
||||
|
||||
queue: None,
|
||||
}),
|
||||
io: IrqSafeSpinlock::new(ProcessIo::new()),
|
||||
});
|
||||
@ -230,8 +234,14 @@ impl Process {
|
||||
/// # Panics
|
||||
///
|
||||
/// Currently, the code will panic if the process is queued/executing on any queue.
|
||||
pub fn enqueue_to(self: Rc<Self>, queue: &CpuQueue) {
|
||||
pub fn enqueue_to(self: Rc<Self>, queue: &'static CpuQueue) {
|
||||
let _irq = IrqGuard::acquire();
|
||||
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
let old_queue = inner.queue.replace(queue);
|
||||
assert!(old_queue.is_none());
|
||||
}
|
||||
let current_state = self.state.swap(ProcessState::Ready, Ordering::SeqCst);
|
||||
|
||||
if current_state == ProcessState::Terminated {
|
||||
@ -243,15 +253,16 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks the process as suspended, blocking it from being run until it's resumed.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The process may not halt its execution immediately when this function is called, only when
|
||||
/// this function is called targeting the *current process* running on *local* CPU.
|
||||
pub fn suspend(&self) {
|
||||
fn dequeue(&self, new_state: ProcessState) {
|
||||
let _irq = IrqGuard::acquire();
|
||||
let current_state = self.state.swap(ProcessState::Suspended, Ordering::SeqCst);
|
||||
assert_ne!(new_state, ProcessState::Ready);
|
||||
assert_ne!(new_state, ProcessState::Running);
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
let current_state = self.state.swap(new_state, Ordering::SeqCst);
|
||||
let proc_queue = inner.queue.take().unwrap();
|
||||
|
||||
proc_queue.dequeue(self.id());
|
||||
|
||||
match current_state {
|
||||
// NOTE: I'm not sure if the process could've been queued between the store and this
|
||||
@ -267,6 +278,8 @@ impl Process {
|
||||
let queue = Cpu::local().queue();
|
||||
|
||||
if cpu_id == local_cpu_id {
|
||||
assert_eq!(queue as *const _, proc_queue as *const _, "Process queue mismatch: process says cpu{}, queue {:p}, actual cpu{}, queue {:p}", cpu_id, proc_queue, local_cpu_id, queue);
|
||||
drop(inner);
|
||||
// Suspending a process running on local CPU
|
||||
unsafe { queue.yield_cpu() }
|
||||
} else {
|
||||
@ -276,6 +289,16 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks the process as suspended, blocking it from being run until it's resumed.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The process may not halt its execution immediately when this function is called, only when
|
||||
/// this function is called targeting the *current process* running on *local* CPU.
|
||||
pub fn suspend(&self) {
|
||||
self.dequeue(ProcessState::Suspended);
|
||||
}
|
||||
|
||||
/// Returns current wait status of the task
|
||||
pub fn wait_status(&self) -> WaitStatus {
|
||||
self.inner.lock().wait_status
|
||||
@ -319,9 +342,7 @@ impl Process {
|
||||
|
||||
/// Handles the cleanup of an exited process
|
||||
pub fn handle_exit(&self) {
|
||||
// Queue lock is still held
|
||||
assert_eq!(self.state(), ProcessState::Terminated);
|
||||
|
||||
// Scheduler still holds a lock of this process?
|
||||
// TODO cancel Wait if a process was killed while suspended?
|
||||
{
|
||||
let inner = self.inner.lock();
|
||||
@ -422,21 +443,25 @@ impl CurrentProcess {
|
||||
/// Terminate the current process
|
||||
pub fn exit(&self, status: ExitCode) {
|
||||
self.inner.lock().exit_status = status.into();
|
||||
let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst);
|
||||
assert_eq!(current_state, ProcessState::Running);
|
||||
debugln!("Process {} exited with code {:?}", self.id(), status);
|
||||
|
||||
match current_state {
|
||||
ProcessState::Suspended => {
|
||||
todo!();
|
||||
}
|
||||
ProcessState::Ready => todo!(),
|
||||
ProcessState::Running => {
|
||||
self.handle_exit();
|
||||
unsafe { Cpu::local().queue().yield_cpu() }
|
||||
}
|
||||
ProcessState::Terminated => todo!(),
|
||||
}
|
||||
self.dequeue(ProcessState::Terminated);
|
||||
|
||||
// let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst);
|
||||
// assert_eq!(current_state, ProcessState::Running);
|
||||
|
||||
// match current_state {
|
||||
// ProcessState::Suspended => {
|
||||
// todo!();
|
||||
// }
|
||||
// ProcessState::Ready => todo!(),
|
||||
// ProcessState::Running => {
|
||||
// self.handle_exit();
|
||||
// unsafe { Cpu::local().queue().yield_cpu() }
|
||||
// }
|
||||
// ProcessState::Terminated => todo!(),
|
||||
// }
|
||||
}
|
||||
|
||||
/// Sets up a return frame to handle a pending signal, if any is present in the task's queue.
|
||||
|
@ -44,6 +44,7 @@ pub struct CpuQueueInner {
|
||||
pub struct CpuQueue {
|
||||
inner: IrqSafeSpinlock<CpuQueueInner>,
|
||||
idle: TaskContext,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
static QUEUES: OneTimeInit<Vec<CpuQueue>> = OneTimeInit::new();
|
||||
@ -87,18 +88,13 @@ impl CpuQueueInner {
|
||||
ProcessState::Ready => {
|
||||
return Some(task);
|
||||
}
|
||||
// Drop suspended tasks from the queue
|
||||
ProcessState::Suspended | ProcessState::Terminated => (),
|
||||
ProcessState::Running => {
|
||||
// TODO fix this finally
|
||||
}
|
||||
// e => panic!(
|
||||
// "Unexpected process state in CpuQueue: {:?} ({} {:?}, cpu_id={})",
|
||||
// e,
|
||||
// task.id(),
|
||||
// task.name(),
|
||||
// task.cpu_id()
|
||||
// ),
|
||||
e => panic!(
|
||||
"Unexpected process state in CpuQueue: {:?} ({} {:?}, cpu_id={})",
|
||||
e,
|
||||
task.id(),
|
||||
task.name(),
|
||||
task.cpu_id()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +110,7 @@ impl CpuQueueInner {
|
||||
|
||||
impl CpuQueue {
|
||||
/// Constructs an empty queue with its own idle task
|
||||
pub fn new() -> Self {
|
||||
pub fn new(index: usize) -> Self {
|
||||
let idle = TaskContext::kernel(__idle, Cpu::local_id() as usize)
|
||||
.expect("Could not construct an idle task");
|
||||
|
||||
@ -127,6 +123,7 @@ impl CpuQueue {
|
||||
})
|
||||
},
|
||||
idle,
|
||||
index,
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,8 +161,8 @@ impl CpuQueue {
|
||||
if let Some(current) = current.as_ref() {
|
||||
if current.state() == ProcessState::Running {
|
||||
current.set_state(ProcessState::Ready);
|
||||
}
|
||||
inner.queue.push_back(current.clone());
|
||||
}
|
||||
|
||||
// inner.stats.cpu_time += delta;
|
||||
} else {
|
||||
@ -209,6 +206,7 @@ impl CpuQueue {
|
||||
|
||||
// log_print_raw!(crate::debug::LogLevel::Info, "\n");
|
||||
|
||||
assert!(ArchitectureImpl::interrupt_mask());
|
||||
to.switch(from)
|
||||
}
|
||||
|
||||
@ -219,13 +217,19 @@ impl CpuQueue {
|
||||
/// Only meant to be called from Process impl. The function does not set any process accounting
|
||||
/// information, which may lead to invalid states.
|
||||
pub unsafe fn enqueue(&self, p: Rc<Process>) {
|
||||
let mut inner = self.inner.lock();
|
||||
assert!(ArchitectureImpl::interrupt_mask());
|
||||
assert_eq!(p.state(), ProcessState::Ready);
|
||||
self.inner.lock().queue.push_back(p);
|
||||
|
||||
inner.queue.push_back(p);
|
||||
}
|
||||
|
||||
/// Removes process with given PID from the exeuction queue.
|
||||
pub fn dequeue(&self, _pid: ProcessId) {
|
||||
todo!();
|
||||
pub fn dequeue(&self, pid: ProcessId) {
|
||||
assert!(ArchitectureImpl::interrupt_mask());
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
inner.queue.retain(|p| p.id() != pid)
|
||||
}
|
||||
|
||||
/// Returns the queue length at this moment.
|
||||
|
Loading…
x
Reference in New Issue
Block a user