-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[platforms] add axplat-x86-pc from arceos
- Loading branch information
1 parent
513fde4
commit bb4fe0c
Showing
11 changed files
with
819 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Boot application processors into the protected mode. | ||
|
||
# Each non-boot CPU ("AP") is started up in response to a STARTUP | ||
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor | ||
# Specification says that the AP will start in real mode with CS:IP | ||
# set to XY00:0000, where XY is an 8-bit value sent with the | ||
# STARTUP. Thus this code must start at a 4096-byte boundary. | ||
# | ||
# Because this code sets DS to zero, it must sit | ||
# at an address in the low 2^16 bytes. | ||
|
||
.equ pa_ap_start32, ap_start32 - ap_start + {start_page_paddr} | ||
.equ pa_ap_gdt, .Lap_tmp_gdt - ap_start + {start_page_paddr} | ||
.equ pa_ap_gdt_desc, .Lap_tmp_gdt_desc - ap_start + {start_page_paddr} | ||
|
||
.equ stack_ptr, {start_page_paddr} + 0xff0 | ||
.equ entry_ptr, {start_page_paddr} + 0xff8 | ||
|
||
# 0x6000 | ||
.section .text | ||
.code16 | ||
.p2align 12 | ||
.global ap_start | ||
ap_start: | ||
cli | ||
wbinvd | ||
|
||
xor ax, ax | ||
mov ds, ax | ||
mov es, ax | ||
mov ss, ax | ||
mov fs, ax | ||
mov gs, ax | ||
|
||
# load the 64-bit GDT | ||
lgdt [pa_ap_gdt_desc] | ||
|
||
# switch to protected-mode | ||
mov eax, cr0 | ||
or eax, (1 << 0) | ||
mov cr0, eax | ||
|
||
# far jump to 32-bit code. 0x8 is code32 segment selector | ||
ljmp 0x8, offset pa_ap_start32 | ||
|
||
.code32 | ||
ap_start32: | ||
mov esp, [stack_ptr] | ||
mov eax, [entry_ptr] | ||
jmp eax | ||
|
||
.balign 8 | ||
# .type multiboot_header, STT_OBJECT | ||
.Lap_tmp_gdt_desc: | ||
.short .Lap_tmp_gdt_end - .Lap_tmp_gdt - 1 # limit | ||
.long pa_ap_gdt # base | ||
|
||
.balign 16 | ||
.Lap_tmp_gdt: | ||
.quad 0x0000000000000000 # 0x00: null | ||
.quad 0x00cf9b000000ffff # 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) | ||
.quad 0x00af9b000000ffff # 0x10: code segment (base=0, limit=0xfffff, type=64bit code exec/read, DPL=0, 4k) | ||
.quad 0x00cf93000000ffff # 0x18: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) | ||
.Lap_tmp_gdt_end: | ||
|
||
# 0x7000 | ||
.p2align 12 | ||
.global ap_end | ||
ap_end: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
#![allow(dead_code)] | ||
|
||
use core::{cell::SyncUnsafeCell, mem::MaybeUninit}; | ||
|
||
use kspin::SpinNoIrq; | ||
use lazyinit::LazyInit; | ||
use memory_addr::PhysAddr; | ||
use x2apic::ioapic::IoApic; | ||
use x2apic::lapic::{LocalApic, LocalApicBuilder, xapic_base}; | ||
use x86_64::instructions::port::Port; | ||
|
||
use self::vectors::*; | ||
use crate::mem::phys_to_virt; | ||
|
||
pub(super) mod vectors { | ||
pub const APIC_TIMER_VECTOR: u8 = 0xf0; | ||
pub const APIC_SPURIOUS_VECTOR: u8 = 0xf1; | ||
pub const APIC_ERROR_VECTOR: u8 = 0xf2; | ||
} | ||
|
||
/// The maximum number of IRQs. | ||
pub const MAX_IRQ_COUNT: usize = 256; | ||
|
||
/// The timer IRQ number. | ||
pub const TIMER_IRQ_NUM: usize = APIC_TIMER_VECTOR as usize; | ||
|
||
const IO_APIC_BASE: PhysAddr = pa!(0xFEC0_0000); | ||
|
||
static LOCAL_APIC: SyncUnsafeCell<MaybeUninit<LocalApic>> = | ||
SyncUnsafeCell::new(MaybeUninit::uninit()); | ||
static mut IS_X2APIC: bool = false; | ||
static IO_APIC: LazyInit<SpinNoIrq<IoApic>> = LazyInit::new(); | ||
|
||
/// Enables or disables the given IRQ. | ||
#[cfg(feature = "irq")] | ||
pub fn set_enable(vector: usize, enabled: bool) { | ||
// should not affect LAPIC interrupts | ||
if vector < APIC_TIMER_VECTOR as _ { | ||
unsafe { | ||
if enabled { | ||
IO_APIC.lock().enable_irq(vector as u8); | ||
} else { | ||
IO_APIC.lock().disable_irq(vector as u8); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Registers an IRQ handler for the given IRQ. | ||
/// | ||
/// It also enables the IRQ if the registration succeeds. It returns `false` if | ||
/// the registration failed. | ||
#[cfg(feature = "irq")] | ||
pub fn register_handler(vector: usize, handler: crate::irq::IrqHandler) -> bool { | ||
crate::irq::register_handler_common(vector, handler) | ||
} | ||
|
||
/// Dispatches the IRQ. | ||
/// | ||
/// This function is called by the common interrupt handler. It looks | ||
/// up in the IRQ handler table and calls the corresponding handler. If | ||
/// necessary, it also acknowledges the interrupt controller after handling. | ||
#[cfg(feature = "irq")] | ||
pub fn dispatch_irq(vector: usize) { | ||
crate::irq::dispatch_irq_common(vector); | ||
unsafe { local_apic().end_of_interrupt() }; | ||
} | ||
|
||
pub(super) fn local_apic<'a>() -> &'a mut LocalApic { | ||
// It's safe as `LOCAL_APIC` is initialized in `init_primary`. | ||
unsafe { LOCAL_APIC.get().as_mut().unwrap().assume_init_mut() } | ||
} | ||
|
||
pub(super) fn raw_apic_id(id_u8: u8) -> u32 { | ||
if unsafe { IS_X2APIC } { | ||
id_u8 as u32 | ||
} else { | ||
(id_u8 as u32) << 24 | ||
} | ||
} | ||
|
||
fn cpu_has_x2apic() -> bool { | ||
match raw_cpuid::CpuId::new().get_feature_info() { | ||
Some(finfo) => finfo.has_x2apic(), | ||
None => false, | ||
} | ||
} | ||
|
||
pub(super) fn init_primary() { | ||
info!("Initialize Local APIC..."); | ||
|
||
unsafe { | ||
// Disable 8259A interrupt controllers | ||
Port::<u8>::new(0x21).write(0xff); | ||
Port::<u8>::new(0xA1).write(0xff); | ||
} | ||
|
||
let mut builder = LocalApicBuilder::new(); | ||
builder | ||
.timer_vector(APIC_TIMER_VECTOR as _) | ||
.error_vector(APIC_ERROR_VECTOR as _) | ||
.spurious_vector(APIC_SPURIOUS_VECTOR as _); | ||
|
||
if cpu_has_x2apic() { | ||
info!("Using x2APIC."); | ||
unsafe { IS_X2APIC = true }; | ||
} else { | ||
info!("Using xAPIC."); | ||
let base_vaddr = phys_to_virt(pa!(unsafe { xapic_base() } as usize)); | ||
builder.set_xapic_base(base_vaddr.as_usize() as u64); | ||
} | ||
|
||
let mut lapic = builder.build().unwrap(); | ||
unsafe { | ||
lapic.enable(); | ||
LOCAL_APIC.get().as_mut().unwrap().write(lapic); | ||
} | ||
|
||
info!("Initialize IO APIC..."); | ||
let io_apic = unsafe { IoApic::new(phys_to_virt(IO_APIC_BASE).as_usize() as u64) }; | ||
IO_APIC.init_once(SpinNoIrq::new(io_apic)); | ||
} | ||
|
||
#[cfg(feature = "smp")] | ||
pub(super) fn init_secondary() { | ||
unsafe { local_apic().enable() }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use core::arch::global_asm; | ||
|
||
use x86_64::registers::control::{Cr0Flags, Cr4Flags}; | ||
use x86_64::registers::model_specific::EferFlags; | ||
|
||
use axconfig::{TASK_STACK_SIZE, plat::PHYS_VIRT_OFFSET}; | ||
|
||
/// Flags set in the ’flags’ member of the multiboot header. | ||
/// | ||
/// (bits 1, 16: memory information, address fields in header) | ||
const MULTIBOOT_HEADER_FLAGS: usize = 0x0001_0002; | ||
|
||
/// The magic field should contain this. | ||
const MULTIBOOT_HEADER_MAGIC: usize = 0x1BADB002; | ||
|
||
/// This should be in EAX. | ||
pub(super) const MULTIBOOT_BOOTLOADER_MAGIC: usize = 0x2BADB002; | ||
|
||
const CR0: u64 = Cr0Flags::PROTECTED_MODE_ENABLE.bits() | ||
| Cr0Flags::MONITOR_COPROCESSOR.bits() | ||
| Cr0Flags::NUMERIC_ERROR.bits() | ||
| Cr0Flags::WRITE_PROTECT.bits() | ||
| Cr0Flags::PAGING.bits(); | ||
const CR4: u64 = Cr4Flags::PHYSICAL_ADDRESS_EXTENSION.bits() | ||
| Cr4Flags::PAGE_GLOBAL.bits() | ||
| if cfg!(feature = "fp_simd") { | ||
Cr4Flags::OSFXSR.bits() | Cr4Flags::OSXMMEXCPT_ENABLE.bits() | ||
} else { | ||
0 | ||
}; | ||
const EFER: u64 = EferFlags::LONG_MODE_ENABLE.bits() | EferFlags::NO_EXECUTE_ENABLE.bits(); | ||
|
||
#[unsafe(link_section = ".bss.stack")] | ||
static mut BOOT_STACK: [u8; TASK_STACK_SIZE] = [0; TASK_STACK_SIZE]; | ||
|
||
global_asm!( | ||
include_str!("multiboot.S"), | ||
mb_magic = const MULTIBOOT_BOOTLOADER_MAGIC, | ||
mb_hdr_magic = const MULTIBOOT_HEADER_MAGIC, | ||
mb_hdr_flags = const MULTIBOOT_HEADER_FLAGS, | ||
entry = sym super::rust_entry, | ||
entry_secondary = sym super::rust_entry_secondary, | ||
|
||
offset = const PHYS_VIRT_OFFSET, | ||
boot_stack_size = const TASK_STACK_SIZE, | ||
boot_stack = sym BOOT_STACK, | ||
|
||
cr0 = const CR0, | ||
cr4 = const CR4, | ||
efer_msr = const x86::msr::IA32_EFER, | ||
efer = const EFER, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
//! Description tables (per-CPU GDT, per-CPU ISS, IDT) | ||
use crate::arch::{GdtStruct, IdtStruct, TaskStateSegment}; | ||
use lazyinit::LazyInit; | ||
|
||
static IDT: LazyInit<IdtStruct> = LazyInit::new(); | ||
|
||
#[percpu::def_percpu] | ||
static TSS: LazyInit<TaskStateSegment> = LazyInit::new(); | ||
|
||
#[percpu::def_percpu] | ||
static GDT: LazyInit<GdtStruct> = LazyInit::new(); | ||
|
||
fn init_percpu() { | ||
unsafe { | ||
IDT.load(); | ||
let tss = TSS.current_ref_mut_raw(); | ||
let gdt = GDT.current_ref_mut_raw(); | ||
tss.init_once(TaskStateSegment::new()); | ||
gdt.init_once(GdtStruct::new(tss)); | ||
gdt.load(); | ||
gdt.load_tss(); | ||
} | ||
} | ||
|
||
/// Initializes IDT, GDT on the primary CPU. | ||
pub(super) fn init_primary() { | ||
axlog::ax_println!("\nInitialize IDT & GDT..."); | ||
IDT.init_once(IdtStruct::new()); | ||
init_percpu(); | ||
} | ||
|
||
/// Initializes IDT, GDT on secondary CPUs. | ||
#[cfg(feature = "smp")] | ||
pub(super) fn init_secondary() { | ||
init_percpu(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// TODO: get memory regions from multiboot info. | ||
|
||
use crate::mem::{MemRegion, MemRegionFlags}; | ||
|
||
/// Returns platform-specific memory regions. | ||
pub(crate) fn platform_regions() -> impl Iterator<Item = MemRegion> { | ||
core::iter::once(MemRegion { | ||
paddr: pa!(0x1000), | ||
size: 0x9e000, | ||
flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE, | ||
name: "low memory", | ||
}) | ||
.chain(crate::mem::default_free_regions()) | ||
.chain(crate::mem::default_mmio_regions()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use x86_64::instructions::port::PortWriteOnly; | ||
|
||
/// Shutdown the whole system (in QEMU), including all CPUs. | ||
/// | ||
/// See <https://wiki.osdev.org/Shutdown> for more information. | ||
pub fn terminate() -> ! { | ||
info!("Shutting down..."); | ||
|
||
#[cfg(platform = "x86_64-pc-oslab")] | ||
{ | ||
axlog::ax_println!("System will reboot, press any key to continue ..."); | ||
let mut buffer = [0u8; 1]; | ||
while super::console::read_bytes(&mut buffer) == 0 {} | ||
axlog::ax_println!("Rebooting ..."); | ||
unsafe { PortWriteOnly::new(0x64).write(0xfeu8) }; | ||
} | ||
|
||
#[cfg(platform = "x86_64-qemu-q35")] | ||
unsafe { | ||
PortWriteOnly::new(0x604).write(0x2000u16) | ||
}; | ||
|
||
crate::arch::halt(); | ||
warn!("It should shutdown!"); | ||
loop { | ||
crate::arch::halt(); | ||
} | ||
} |
Oops, something went wrong.