Skip to content

Commit 380c7d6

Browse files
committed
wip
1 parent 67f383a commit 380c7d6

File tree

6 files changed

+330
-4
lines changed

6 files changed

+330
-4
lines changed

hal-x86_64/src/cpu.rs

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub mod intrinsics;
66
#[cfg(feature = "alloc")]
77
pub mod local;
88
pub mod msr;
9+
pub mod smp;
10+
pub mod topology;
911
pub use self::msr::Msr;
1012

1113
#[repr(transparent)]

hal-x86_64/src/cpu/local.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Msr;
1+
use super::{topology::Processor, Msr};
22
use alloc::boxed::Box;
33
use core::{
44
arch::asm,
@@ -16,6 +16,7 @@ pub struct GsLocalData {
1616
/// from `gs:0x0` to get the local data's address.
1717
_self: *const Self,
1818
magic: usize,
19+
processor: Processor,
1920
/// Because this struct is self-referential, it may not be `Unpin`.
2021
_must_pin: PhantomPinned,
2122
/// Arbitrary user data.
@@ -35,12 +36,13 @@ impl GsLocalData {
3536
const MAGIC: usize = 0xC0FFEE;
3637
pub const MAX_LOCAL_KEYS: usize = 64;
3738

38-
const fn new() -> Self {
39+
pub(crate) const fn new(processor: Processor) -> Self {
3940
#[allow(clippy::declare_interior_mutable_const)] // array initializer
4041
const LOCAL_SLOT_INIT: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
4142
Self {
4243
_self: ptr::null(),
4344
_must_pin: PhantomPinned,
45+
processor,
4446
magic: Self::MAGIC,
4547
userdata: [LOCAL_SLOT_INIT; Self::MAX_LOCAL_KEYS],
4648
}
@@ -78,6 +80,10 @@ impl GsLocalData {
7880
.expect("GsLocalData::current() called before local data was initialized on this core!")
7981
}
8082

83+
pub fn processor_info(&self) -> &Processor {
84+
&self.processor
85+
}
86+
8187
/// Access a local key on this CPU core's local data.
8288
pub fn with<T, U>(&self, key: &LocalKey<T>, f: impl FnOnce(&T) -> U) -> U {
8389
let idx = *key.idx.get();
@@ -108,14 +114,15 @@ impl GsLocalData {
108114
///
109115
/// This should only be called a single time per CPU core.
110116
#[track_caller]
111-
pub fn init() {
117+
pub(crate) fn init(self: Pin<Box<Self>>) {
112118
if Self::has_local_data() {
113119
tracing::warn!("this CPU core already has local data initialized!");
114120
debug_assert!(false, "this CPU core already has local data initialized!");
115121
return;
116122
}
117123

118-
let ptr = Box::into_raw(Box::new(Self::new()));
124+
let this = unsafe { Pin::into_inner_unchecked(self) };
125+
let ptr = Box::into_raw(this);
119126
tracing::trace!(?ptr, "initializing local data");
120127
unsafe {
121128
// set up self reference

hal-x86_64/src/cpu/smp.rs

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use crate::{
2+
control_regs::{Cr0, Cr4},
3+
cpu::{
4+
msr::{Efer, Msr},
5+
topology, Ring,
6+
},
7+
segment,
8+
};
9+
use core::arch::global_asm;
10+
use mycelium_util::bits;
11+
pub fn bringup() -> Result<(), ()> {
12+
unsafe {
13+
tracing::info!(
14+
"AP trampoline: {:p} .. {:p}",
15+
&AP_TRAMPOLINE_START,
16+
&AP_TRAMPOLINE_END
17+
);
18+
assert_eq!(
19+
&AP_TRAMPOLINE_START as *const _ as usize,
20+
AP_TRAMPOLINE_ADDR
21+
);
22+
}
23+
24+
Ok(())
25+
}
26+
27+
extern "C" {
28+
#[link_name = "ap_trampoline"]
29+
static AP_TRAMPOLINE_START: u8;
30+
#[link_name = "ap_trampoline_end"]
31+
static AP_TRAMPOLINE_END: u8;
32+
}
33+
34+
const AP_TRAMPOLINE_ADDR: usize = 0x8000;
35+
global_asm! {
36+
// /!\ EXTREMELY MESSED UP HACK: stick this in the `.boot-first-stage`
37+
// section that's defined by the `bootloader` crate's linker script, so that
38+
// it gets linked into 16-bit memory. we don't control the linker script, so
39+
// we can't define our own section and stick it in the right place, but we
40+
// can piggyback off of `bootloader`'s linker script.
41+
//
42+
// OBVIOUSLY THIS WILL CRASH AND BURN IF YOU ARENT LINKING WITH `bootloader`
43+
// BUT WHATEVER LOL THATS NOT MY PROBLEM,,,
44+
".section .boot-first-stage, \"wx\"",
45+
".code16",
46+
".org {trampoline_addr}",
47+
".align 4096",
48+
".global ap_trampoline",
49+
".global ap_trampoline_end",
50+
"ap_trampoline:",
51+
" jmp short ap_start",
52+
" .nops 8",
53+
"ap_spinlock: .quad 0",
54+
"ap_pml4: .quad 0",
55+
56+
"ap_start:",
57+
" cli",
58+
59+
// zero segment registers
60+
" xor ax, ax",
61+
" mov ds, ax",
62+
" mov es, ax",
63+
" mov ss, ax",
64+
65+
// initialize stack pointer to an invalid (null) value
66+
" mov sp, 0x0",
67+
68+
// setup page table
69+
"mov eax, [ap_pml4]",
70+
"mov edi, [eax]",
71+
"mov cr3, edi",
72+
73+
// init FPU
74+
" fninit",
75+
76+
// load 32-bit GDT
77+
" lgdt [gdt32_ptr]",
78+
79+
// set CR4 flags
80+
" mov eax, cr4",
81+
" or eax, {cr4flags}",
82+
" mov cr4, eax",
83+
84+
// enable long mode in EFER
85+
" mov ecx, {efer_num}",
86+
" rdmsr",
87+
" or eax, {efer_bits}",
88+
" wrmsr",
89+
90+
// set CR0 flags to enable paging and write protection
91+
" mov ebx, cr0",
92+
" or ebx, {cr0flags}",
93+
" mov cr0, ebx",
94+
95+
// 32-bit GDT
96+
".align 16",
97+
"gdt32:",
98+
// TODO(eliza): would be nice to build the bits of the GDT entries in
99+
// Rust...
100+
" .long 0, 0",
101+
" .quad {gdt32_code}", // code segment
102+
" .quad {gdt32_data}", // data segment
103+
" .long 0x00000068, 0x00CF8900", // TSS
104+
"gdt32_ptr:",
105+
" .word gdt32_ptr - gdt32 - 1", // size
106+
" .word gdt32", // offset
107+
"ap_trampoline_end:",
108+
// ".code64", // reset to 64 bit code when exiting the asm block
109+
trampoline_addr = const AP_TRAMPOLINE_ADDR,
110+
cr4flags = const AP_CR4,
111+
cr0flags = const AP_CR0,
112+
efer_num = const Msr::ia32_efer().num,
113+
efer_bits = const EFER_LONG_MODE,
114+
gdt32_code = const segment::Descriptor::code_32()
115+
.with_ring(Ring::Ring0).bits(),
116+
gdt32_data = const segment::Descriptor::data_flat_16()
117+
.bits(),
118+
}
119+
120+
/// Initial CR4 flags to set for an application processor.
121+
const AP_CR4: u32 = bits::Pack64::pack_in(0)
122+
.set_all(&Cr4::PAGE_SIZE_EXTENSION)
123+
.set_all(&Cr4::PHYSICAL_ADDRESS_EXTENSION)
124+
.set_all(&Cr4::PAGE_GLOBAL_ENABLE)
125+
.set_all(&Cr4::OSFXSR)
126+
.bits() as u32;
127+
128+
/// Initial CR0 flags to set for an application processor.
129+
const AP_CR0: u32 = bits::Pack64::pack_in(0)
130+
.set_all(&Cr0::PROTECTED_MODE_ENABLE)
131+
.set_all(&Cr0::PAGING_ENABLE)
132+
.set_all(&Cr0::WRITE_PROTECT)
133+
.bits() as u32;
134+
135+
/// EFER bits to enable long mode
136+
const EFER_LONG_MODE: u32 = bits::Pack64::pack_in(0)
137+
.set_all(&Efer::LONG_MODE_ENABLE)
138+
.set_all(&Efer::NO_EXECUTE_ENABLE)
139+
.bits() as u32;

hal-x86_64/src/cpu/topology.rs

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use crate::segment;
2+
use alloc::{boxed::Box, vec::Vec};
3+
use core::fmt;
4+
5+
pub const MAX_CPUS: usize = 256;
6+
7+
pub type Id = usize;
8+
9+
#[derive(Debug)]
10+
pub struct Topology {
11+
pub(crate) boot_processor: Processor,
12+
pub(crate) application_processors: Vec<Processor>,
13+
}
14+
15+
#[derive(Debug, Clone, Eq, PartialEq)]
16+
#[non_exhaustive]
17+
pub struct Processor {
18+
pub id: Id,
19+
pub device_uid: u32,
20+
pub lapic_id: u32,
21+
}
22+
23+
#[derive(Debug, Clone, PartialEq, Eq)]
24+
#[non_exhaustive]
25+
pub enum TopologyError {
26+
NoTopology,
27+
Weird(&'static str),
28+
}
29+
30+
impl Topology {
31+
// TODO(eliza): good error type
32+
#[tracing::instrument(name = "Topology::from_acpi", skip(acpi), err(Display))]
33+
pub fn from_acpi(acpi: &acpi::PlatformInfo) -> Result<Self, TopologyError> {
34+
use acpi::platform;
35+
36+
let platform::ProcessorInfo {
37+
ref application_processors,
38+
ref boot_processor,
39+
} = acpi
40+
.processor_info
41+
.as_ref()
42+
.ok_or(TopologyError::NoTopology)?;
43+
44+
let bsp = Processor {
45+
id: 0,
46+
device_uid: boot_processor.processor_uid,
47+
lapic_id: boot_processor.local_apic_id,
48+
};
49+
50+
if boot_processor.is_ap {
51+
return Err(TopologyError::Weird(
52+
"boot processor claimed to be an application processor",
53+
))?;
54+
}
55+
56+
if boot_processor.state != platform::ProcessorState::Running {
57+
return Err(TopologyError::Weird(
58+
"boot processor claimed to not be running",
59+
))?;
60+
}
61+
62+
tracing::info!(
63+
bsp.id,
64+
bsp.device_uid,
65+
bsp.lapic_id,
66+
"boot processor seems normalish"
67+
);
68+
69+
let mut id = 1;
70+
let mut disabled = 0;
71+
let mut aps = Vec::with_capacity(application_processors.len());
72+
for ap in application_processors {
73+
if !ap.is_ap {
74+
return Err(TopologyError::Weird(
75+
"application processor claimed to be the boot processor",
76+
))?;
77+
}
78+
79+
match ap.state {
80+
// if the firmware disabled a processor, just skip it
81+
platform::ProcessorState::Disabled => {
82+
tracing::warn!(
83+
ap.device_uid = ap.processor_uid,
84+
"application processor disabled by firmware, skipping it"
85+
);
86+
disabled += 1;
87+
continue;
88+
}
89+
// if a processor claims it's already running, that seems messed up!
90+
platform::ProcessorState::Running => {
91+
return Err(TopologyError::Weird(
92+
"application processors should not be running yet",
93+
));
94+
}
95+
// otherwise, add it to the topology
96+
platform::ProcessorState::WaitingForSipi => {}
97+
}
98+
99+
let ap = Processor {
100+
id,
101+
device_uid: ap.processor_uid,
102+
lapic_id: ap.local_apic_id,
103+
};
104+
tracing::debug!(
105+
ap.id,
106+
ap.device_uid,
107+
ap.lapic_id,
108+
"found application processor"
109+
);
110+
111+
aps.push(ap);
112+
id += 1;
113+
}
114+
115+
tracing::info!(
116+
"found {} application processors ({} disabled)",
117+
application_processors.len(),
118+
disabled,
119+
);
120+
121+
Ok(Self {
122+
application_processors: aps,
123+
boot_processor: bsp,
124+
})
125+
}
126+
}
127+
128+
impl Processor {
129+
pub(crate) fn init_processor<const GDT_SIZE: usize>(&self, gdt: &mut segment::Gdt<GDT_SIZE>) {
130+
tracing::info!(self.id, "initializing processor");
131+
use super::local::GsLocalData;
132+
Box::pin(GsLocalData::new(self.clone())).init();
133+
}
134+
}
135+
136+
impl fmt::Display for TopologyError {
137+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138+
match self {
139+
TopologyError::NoTopology => f.pad("no topology information found in MADT"),
140+
TopologyError::Weird(msg) => {
141+
write!(f, "found something weird: {msg}, is the MADT corrupted?")
142+
}
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)