Skip to main content

someboot/
lib.rs

1#![no_std]
2#![cfg_attr(not(test), no_main)]
3#![cfg_attr(target_arch = "x86_64", feature(abi_x86_interrupt))]
4
5#[allow(unused_imports)]
6#[macro_use]
7extern crate alloc;
8
9#[macro_use]
10extern crate core;
11
12#[macro_use]
13extern crate log;
14
15#[macro_use]
16pub mod console;
17
18#[cfg(target_arch = "loongarch64")]
19#[path = "arch/loongarch64/mod.rs"]
20pub mod arch;
21
22#[cfg(target_arch = "aarch64")]
23#[path = "arch/aarch64/mod.rs"]
24pub mod arch;
25
26#[cfg(target_arch = "x86_64")]
27#[path = "arch/x86_64/mod.rs"]
28pub mod arch;
29
30#[cfg(target_arch = "riscv64")]
31#[path = "arch/riscv64/mod.rs"]
32pub mod arch;
33
34mod acpi;
35mod cmdline;
36pub(crate) mod consts;
37#[cfg(efi)]
38mod efi_stub;
39mod elf;
40mod entropy;
41mod entry;
42mod err;
43pub(crate) mod fdt;
44pub mod irq;
45pub mod mem;
46pub mod power;
47
48pub mod rtc;
49pub mod smp;
50pub mod timer;
51
52pub use acpi::rsdp_addr_phys;
53pub use cmdline::cmdline;
54pub use entropy::boot_entropy;
55pub use fdt::{fdt_addr, fdt_addr_phys, platform_name};
56pub use page_table_generic::*;
57pub use somehal_macros::{entry, someboot_secondary_entry as secondary_entry};
58
59use crate::{
60    irq::IrqId,
61    mem::{PageTableInfo, cpu_area_phys_to_virt},
62    power::CpuOnError,
63};
64
65#[allow(unused)]
66pub trait ArchTrait {
67    type P: TableMeta;
68    type Console: console::ArchConsoleOps;
69
70    fn _va(paddr: usize) -> *mut u8;
71    fn _io(paddr: usize) -> *mut u8 {
72        Self::_va(paddr)
73    }
74    fn ioremap_device(_addr: usize, _size: usize) -> Option<*mut u8> {
75        None
76    }
77    fn cpu_area_phys_to_virt(paddr: usize) -> *mut u8 {
78        Self::_va(paddr)
79    }
80
81    fn cpu_current_hartid() -> usize;
82
83    fn jump_to(entry: usize, sp: usize) -> !;
84
85    fn post_allocator();
86
87    fn init_boot_tls() {}
88
89    fn per_cpu_trap_init(is_primary: bool);
90    fn trap_addr() -> usize;
91
92    fn virt_to_phys(vaddr: *const u8) -> usize;
93
94    fn canonicalize_paddr(addr: usize) -> usize {
95        addr
96    }
97    fn user_aspace_needs_kernel_mappings() -> bool {
98        true
99    }
100
101    fn kernel_space() -> core::ops::Range<usize>;
102    fn is_kernel_relocated_at(addr: usize) -> bool {
103        (crate::consts::VM_LOAD_ADDRESS..usize::MAX).contains(&addr)
104    }
105
106    fn is_mmu_enabled() -> bool;
107
108    fn kernel_page_table() -> PageTableInfo;
109    fn set_kernel_page_table(val: PageTableInfo);
110    #[cfg(uspace)]
111    fn user_page_table() -> PageTableInfo;
112    #[cfg(uspace)]
113    fn set_user_page_table(val: PageTableInfo);
114
115    fn shutdown() -> !;
116    fn reset() -> ! {
117        Self::shutdown()
118    }
119    fn secondary_entry_fn_address() -> *const ();
120    /// Delivers the architecture-specific wake request to one secondary CPU.
121    ///
122    /// This method owns only the hardware or firmware transport. The generic
123    /// someboot lifecycle publishes `KICKED`, waits for the target CPU to
124    /// report `ALIVE`, and releases it into the OS entry path.
125    fn kick_secondary_cpu(hartid: usize, entry: usize, arg: usize) -> Result<(), CpuOnError>;
126
127    /// Get the timer frequency in Hz
128    fn systimer_freq() -> usize;
129    /// Get the current timer tick count
130    fn systimer_tick() -> usize;
131    /// Reports whether the timer counter is a synchronized system counter.
132    fn systimer_stability() -> timer::CounterStability;
133
134    fn irq_all_is_enabled() -> bool;
135    fn irq_all_set_enable(enable: bool);
136
137    fn dcache_range(op: DCacheOp, addr: usize, size: usize);
138
139    /// Prepare cached pages before creating an uncached DMA alias.
140    fn dma_coherent_before_map_uncached(addr: usize, size: usize) {
141        Self::dcache_range(DCacheOp::CleanInvalidate, addr, size);
142    }
143
144    /// Order accesses before removing an uncached DMA alias.
145    fn dma_coherent_before_unmap_uncached(_addr: usize, _size: usize) {}
146
147    /// Complete ordering after a DMA coherent alias update.
148    fn dma_coherent_after_mapping_update() {}
149
150    /// EFI 入口点 - 从 EFI PE 入口跳转到内核
151    ///
152    /// Returns `false` on architectures without EFI handoff.
153    ///
154    /// # Safety
155    /// `system_table` 必须是当前 EFI 固件提供的有效 `EFI_SYSTEM_TABLE` 指针,
156    /// 并且调用者必须保证此调用符合对应架构的启动约定。
157    unsafe fn efi_enter_kernel(_system_table: *const ::core::ffi::c_void) -> bool {
158        false
159    }
160}
161
162/// System-timer arming capability for architectures whose timer is hardware
163/// independent of the interrupt controller (Arm generic timer, RISC-V SBI
164/// timer, LoongArch TCG).
165///
166/// The counter domain (`systimer_freq`/`systimer_tick`/`systimer_stability`)
167/// stays on [`ArchTrait`] because every architecture provides a counter. On
168/// x86_64 the system timer lives inside the local APIC, so somehal's
169/// interrupt-controller driver owns arming and the architecture simply does
170/// not implement this trait — the absent capability is the compile-time
171/// boundary, no conditional compilation is involved.
172///
173/// Primitives are implemented per architecture; the provided methods carry
174/// the common implementations and may be overridden (LoongArch overrides the
175/// per-line IRQ pair for its multi-line ECFG semantics).
176pub trait SystimerArch: ArchTrait {
177    /// The boot-level IRQ line of the system timer.
178    fn systimer_irq_id() -> IrqId;
179    fn systimer_enable();
180    fn systimer_irq_enable();
181    fn systimer_irq_disable();
182    fn systimer_irq_is_enabled() -> bool;
183    /// Set the timer interval in ticks.
184    fn systimer_set_interval(ticks: usize);
185
186    /// Acknowledge and clear the timer interrupt. Timers whose pending state
187    /// clears on re-arming keep this default.
188    fn systimer_ack() {}
189
190    /// Whether one boot-level IRQ line is enabled. The common implementation
191    /// knows only the system-timer line.
192    fn irq_is_enabled(irq: IrqId) -> bool {
193        irq == Self::systimer_irq_id() && Self::systimer_irq_is_enabled()
194    }
195
196    /// Enable or disable one boot-level IRQ line. The common implementation
197    /// controls only the system-timer line and ignores others.
198    fn irq_set_enable(irq: IrqId, enable: bool) {
199        if irq == Self::systimer_irq_id() {
200            if enable {
201                Self::systimer_irq_enable();
202            } else {
203                Self::systimer_irq_disable();
204            }
205        }
206    }
207
208    /// Arms a one-shot deadline `ticks` from now.
209    fn set_next_event_in_ticks(ticks: usize) {
210        Self::systimer_set_interval(ticks);
211    }
212
213    /// Configure the system timer with the desired interval.
214    fn set_next_event(interval: core::time::Duration) {
215        const NANOS_PER_SEC: u128 = 1_000_000_000;
216        let ticks = (interval.as_nanos() * Self::systimer_freq() as u128 / NANOS_PER_SEC) as usize;
217        Self::systimer_set_interval(ticks);
218    }
219}
220
221#[derive(Debug, Clone, Copy)]
222pub enum DCacheOp {
223    Clean,
224    Invalidate,
225    CleanInvalidate,
226}
227
228pub fn post_allocator() {
229    fdt::init_with_alloc();
230    smp::finalize_secondary_boot_metadata();
231    debug!("Setup after allocator");
232    arch::Arch::post_allocator();
233}
234
235/// Returns boot arguments captured from FDT, UEFI load options, or built into the image.
236pub fn bootargs() -> Option<&'static str> {
237    cmdline::cmdline()
238}
239
240/// Get the current kernel page table physical address and ASID
241pub fn kernel_page_table_paddr() -> usize {
242    arch::Arch::kernel_page_table().addr
243}
244
245/// Set the kernel page table physical address and ASID
246pub fn set_kernel_page_table_paddr(paddr: usize) {
247    arch::Arch::set_kernel_page_table(PageTableInfo {
248        asid: 0,
249        addr: paddr,
250    });
251}
252
253#[cfg(uspace)]
254pub fn user_page_table() -> PageTableInfo {
255    arch::Arch::user_page_table()
256}
257
258#[cfg(uspace)]
259pub fn set_user_page_table(pt: PageTableInfo) {
260    arch::Arch::set_user_page_table(pt);
261}
262
263/// Entry point after enabling MMU
264fn prime_entry() -> ! {
265    fdt::setup_earlycon();
266    let _ = acpi::earlycon::acpi_setup_earlycon();
267
268    println!("Trap vector at {:#x}", arch::Arch::trap_addr());
269
270    // mem::init_after_mmu();
271    mem::memory_map_setup();
272    mem::print_memory_map();
273
274    smp::initialize_percpu_layout();
275
276    unsafe extern "C" {
277        fn __someboot_main() -> !;
278    }
279
280    let entry = __someboot_main as *const () as usize;
281    let cpu_idx = crate::smp::early_current_cpu_idx();
282    let sp = crate::smp::cpu_meta(cpu_idx).unwrap().stack_top;
283    let sp = cpu_area_phys_to_virt(sp);
284    println!(
285        "Jumping to main entry point at {:#x} with SP {:#p}",
286        entry, sp
287    );
288    arch::Arch::jump_to(entry, sp as _)
289}