Skip to content

Commit c19b174

Browse files
authored
Merge pull request #1705 from stlankes/smp
enable SMP support for aarch64
2 parents dd0771f + 4abb9eb commit c19b174

File tree

9 files changed

+333
-48
lines changed

9 files changed

+333
-48
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ jobs:
196196
if: matrix.arch == 'x86_64'
197197
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package rusty_demo qemu ${{ matrix.flags }}
198198
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package rusty_demo --smp 4 qemu ${{ matrix.flags }}
199-
# https://github.com/hermit-os/kernel/issues/737
200-
if: matrix.arch != 'aarch64'
201199
# https://github.com/hermit-os/kernel/issues/1286
202200
continue-on-error: ${{ matrix.arch == 'riscv64' }}
203201
- run: cargo xtask ci rs --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} --package rusty_demo --smp 4 qemu ${{ matrix.flags }} --uefi

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ memory_addresses = { version = "0.2.2", default-features = false, features = [
170170

171171
[target.'cfg(target_arch = "aarch64")'.dependencies]
172172
aarch64 = { version = "0.0.14", default-features = false }
173-
arm-gic = { version = "0.3" }
173+
arm-gic = { version = "0.4" }
174174
hermit-dtb = { version = "0.1" }
175175
semihosting = { version = "0.1", optional = true }
176176
memory_addresses = { version = "0.2.2", default-features = false, features = [

src/arch/aarch64/kernel/interrupts.rs

Lines changed: 108 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use core::sync::atomic::{AtomicU64, Ordering};
66

77
use aarch64::regs::*;
88
use ahash::RandomState;
9-
use arm_gic::gicv3::GicV3;
9+
use arm_gic::gicv3::{GicV3, SgiTarget};
1010
use arm_gic::{IntId, Trigger};
1111
use hashbrown::HashMap;
1212
use hermit_dtb::Dtb;
@@ -55,10 +55,7 @@ pub fn enable() {
5555
}
5656
}
5757

58-
/// Enable Interrupts and wait for the next interrupt (HLT instruction)
59-
/// According to <https://lists.freebsd.org/pipermail/freebsd-current/2004-June/029369.html>, this exact sequence of assembly
60-
/// instructions is guaranteed to be atomic.
61-
/// This is important, because another CPU could call wakeup_core right when we decide to wait for the next interrupt.
58+
/// Enable all interrupts and wait for the next interrupt (wfi instruction)
6259
#[inline]
6360
pub fn enable_and_wait() {
6461
unsafe {
@@ -94,13 +91,8 @@ pub(crate) fn install_handlers() {
9491
debug!("Handle timer interrupt");
9592

9693
// disable timer
97-
unsafe {
98-
asm!(
99-
"msr cntp_cval_el0, xzr",
100-
"msr cntp_ctl_el0, xzr",
101-
options(nostack, nomem),
102-
);
103-
}
94+
CNTP_CVAL_EL0.set(0);
95+
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::CLEAR);
10496
}
10597

10698
for (key, value) in get_interrupt_handlers().into_iter() {
@@ -237,8 +229,19 @@ pub(crate) extern "C" fn do_error(_state: &State) -> ! {
237229
scheduler::abort()
238230
}
239231

240-
pub fn wakeup_core(_core_to_wakeup: CoreId) {
241-
todo!("wakeup_core stub");
232+
pub fn wakeup_core(core_id: CoreId) {
233+
debug!("Wakeup core {core_id}");
234+
let reschedid = IntId::sgi(SGI_RESCHED.into());
235+
236+
GicV3::send_sgi(
237+
reschedid,
238+
SgiTarget::List {
239+
affinity3: 0,
240+
affinity2: 0,
241+
affinity1: 0,
242+
target_list: 1 << core_id,
243+
},
244+
);
242245
}
243246

244247
pub(crate) fn init() {
@@ -261,21 +264,31 @@ pub(crate) fn init() {
261264
let (slice, _residual_slice) = residual_slice.split_at(core::mem::size_of::<u64>());
262265
let gicr_size = u64::from_be_bytes(slice.try_into().unwrap());
263266

264-
let gicr_stride = dtb
265-
.get_property("/intc", "redistributor-stride")
266-
.map(|bytes| u64::from_be_bytes(bytes.try_into().unwrap()))
267-
.unwrap_or(0);
268-
269267
let num_cpus = dtb
270268
.enum_subnodes("/cpus")
271269
.filter(|name| name.contains("cpu@"))
272270
.count();
273-
let cpu_id = core_id();
271+
let cpu_id: usize = core_id().try_into().unwrap();
272+
273+
let compatible = core::str::from_utf8(
274+
dtb.get_property("/intc", "compatible")
275+
.unwrap_or(b"unknown"),
276+
)
277+
.unwrap()
278+
.replace('\0', "");
279+
let is_gic_v4 = if compatible == "arm,gic-v4" {
280+
info!("Found GIC v4 with {num_cpus} cpus");
281+
true
282+
} else if compatible == "arm,gic-v3" {
283+
info!("Found GIC v3 with {num_cpus} cpus");
284+
false
285+
} else {
286+
panic!("{compatible} isn't supported")
287+
};
274288

275-
info!("Found {num_cpus} cpus!");
276289
info!("Found GIC Distributor interface at {gicd_start:p} (size {gicd_size:#X})");
277290
info!(
278-
"Found generic interrupt controller redistributor at {gicr_start:p} (size {gicr_size:#X}, stride {gicr_stride:#X})"
291+
"Found generic interrupt controller redistributor at {gicr_start:p} (size {gicr_size:#X})"
279292
);
280293

281294
let gicd_address =
@@ -301,16 +314,16 @@ pub(crate) fn init() {
301314
flags,
302315
);
303316

304-
GicV3::set_priority_mask(0xff);
305317
let mut gic = unsafe {
306318
GicV3::new(
307319
gicd_address.as_mut_ptr(),
308320
gicr_address.as_mut_ptr(),
309321
num_cpus,
310-
gicr_stride as usize,
322+
is_gic_v4,
311323
)
312324
};
313-
gic.setup(cpu_id as usize);
325+
gic.setup(cpu_id);
326+
GicV3::set_priority_mask(0xff);
314327

315328
for node in dtb.enum_subnodes("/") {
316329
let parts: Vec<_> = node.split('@').collect();
@@ -349,27 +362,91 @@ pub(crate) fn init() {
349362
} else {
350363
panic!("Invalid interrupt type");
351364
};
352-
gic.set_interrupt_priority(timer_irqid, Some(cpu_id as usize), 0x00);
365+
gic.set_interrupt_priority(timer_irqid, Some(cpu_id), 0x00);
353366
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
354-
gic.set_trigger(timer_irqid, Some(cpu_id as usize), Trigger::Level);
367+
gic.set_trigger(timer_irqid, Some(cpu_id), Trigger::Level);
355368
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1 {
356-
gic.set_trigger(timer_irqid, Some(cpu_id as usize), Trigger::Edge);
369+
gic.set_trigger(timer_irqid, Some(cpu_id), Trigger::Edge);
357370
} else {
358371
panic!("Invalid interrupt level!");
359372
}
360-
gic.enable_interrupt(timer_irqid, Some(cpu_id as usize), true);
373+
gic.enable_interrupt(timer_irqid, Some(cpu_id), true);
361374
}
362375
}
363376
}
364377

365378
let reschedid = IntId::sgi(SGI_RESCHED.into());
366-
gic.set_interrupt_priority(reschedid, Some(cpu_id as usize), 0x00);
367-
gic.enable_interrupt(reschedid, Some(cpu_id as usize), true);
379+
gic.set_interrupt_priority(reschedid, Some(cpu_id), 0x01);
380+
gic.enable_interrupt(reschedid, Some(cpu_id), true);
368381
IRQ_NAMES.lock().insert(SGI_RESCHED, "Reschedule");
369382

370383
*GIC.lock() = Some(gic);
371384
}
372385

386+
// marks the given CPU core as awake
387+
pub fn init_cpu() {
388+
let cpu_id: usize = core_id().try_into().unwrap();
389+
390+
if let Some(ref mut gic) = *GIC.lock() {
391+
debug!("Mark cpu {cpu_id} as awake");
392+
393+
gic.setup(cpu_id);
394+
GicV3::set_priority_mask(0xff);
395+
396+
let dtb = unsafe {
397+
Dtb::from_raw(ptr::with_exposed_provenance(
398+
env::boot_info().hardware_info.device_tree.unwrap().get() as usize,
399+
))
400+
.expect(".dtb file has invalid header")
401+
};
402+
403+
for node in dtb.enum_subnodes("/") {
404+
let parts: Vec<_> = node.split('@').collect();
405+
406+
if let Some(compatible) = dtb.get_property(parts.first().unwrap(), "compatible") {
407+
if core::str::from_utf8(compatible).unwrap().contains("timer") {
408+
let irq_slice = dtb
409+
.get_property(parts.first().unwrap(), "interrupts")
410+
.unwrap();
411+
/* Secure Phys IRQ */
412+
let (_irqtype, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
413+
let (_irq, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
414+
let (_irqflags, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
415+
/* Non-secure Phys IRQ */
416+
let (irqtype, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
417+
let (irq, irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
418+
let (irqflags, _irq_slice) = irq_slice.split_at(core::mem::size_of::<u32>());
419+
let irqtype = u32::from_be_bytes(irqtype.try_into().unwrap());
420+
let irq = u32::from_be_bytes(irq.try_into().unwrap());
421+
let irqflags = u32::from_be_bytes(irqflags.try_into().unwrap());
422+
423+
// enable timer interrupt
424+
let timer_irqid = if irqtype == 1 {
425+
IntId::ppi(irq)
426+
} else if irqtype == 0 {
427+
IntId::spi(irq)
428+
} else {
429+
panic!("Invalid interrupt type");
430+
};
431+
gic.set_interrupt_priority(timer_irqid, Some(cpu_id), 0x00);
432+
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
433+
gic.set_trigger(timer_irqid, Some(cpu_id), Trigger::Level);
434+
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1 {
435+
gic.set_trigger(timer_irqid, Some(cpu_id), Trigger::Edge);
436+
} else {
437+
panic!("Invalid interrupt level!");
438+
}
439+
gic.enable_interrupt(timer_irqid, Some(cpu_id), true);
440+
}
441+
}
442+
}
443+
444+
let reschedid = IntId::sgi(SGI_RESCHED.into());
445+
gic.set_interrupt_priority(reschedid, Some(cpu_id), 0x01);
446+
gic.enable_interrupt(reschedid, Some(cpu_id), true);
447+
}
448+
}
449+
373450
static IRQ_NAMES: InterruptTicketMutex<HashMap<u8, &'static str, RandomState>> =
374451
InterruptTicketMutex::new(HashMap::with_hasher(RandomState::with_seeds(0, 0, 0, 0)));
375452

src/arch/aarch64/kernel/mod.rs

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,12 @@ pub fn boot_processor_init() {
163163
#[allow(dead_code)]
164164
pub fn application_processor_init() {
165165
CoreLocal::install();
166+
interrupts::init_cpu();
166167
finish_processor_init();
167168
}
168169

169170
fn finish_processor_init() {
170-
debug!("Initialized Processor");
171+
debug!("Initialized processor {}", core_id());
171172

172173
// Allocate stack for the CPU and pass the addresses.
173174
let layout = Layout::from_size_align(KERNEL_STACK_SIZE, BasePageSize::SIZE as usize).unwrap();
@@ -177,7 +178,82 @@ fn finish_processor_init() {
177178
}
178179

179180
pub fn boot_next_processor() {
180-
CPU_ONLINE.0.fetch_add(1, Ordering::Release);
181+
// This triggers to wake up the next processor (bare-metal/QEMU) or uhyve
182+
// to initialize the next processor.
183+
#[allow(unused_variables)]
184+
let cpu_online = CPU_ONLINE.0.fetch_add(1, Ordering::Release);
185+
186+
#[cfg(all(target_os = "none", feature = "smp"))]
187+
if !env::is_uhyve() && get_possible_cpus() > 1 {
188+
use core::arch::asm;
189+
use core::hint::spin_loop;
190+
191+
use hermit_dtb::Dtb;
192+
193+
use crate::kernel::start::{TTBR0, smp_start};
194+
use crate::mm::virtual_to_physical;
195+
196+
if cpu_online == 0 {
197+
let virt_start = VirtAddr::from(smp_start as usize);
198+
let phys_start = virtual_to_physical(virt_start).unwrap();
199+
assert!(virt_start.as_u64() == phys_start.as_u64());
200+
201+
trace!("Virtual address of smp_start 0x{virt_start:x}");
202+
trace!("Physical address of smp_start 0x{phys_start:x}");
203+
204+
let dtb = unsafe {
205+
Dtb::from_raw(core::ptr::with_exposed_provenance(
206+
env::boot_info().hardware_info.device_tree.unwrap().get() as usize,
207+
))
208+
.expect(".dtb file has invalid header")
209+
};
210+
211+
let cpu_on = u32::from_be_bytes(
212+
dtb.get_property("/psci", "cpu_on")
213+
.unwrap()
214+
.try_into()
215+
.unwrap(),
216+
);
217+
trace!("CPU_ON: 0x{cpu_on:x}");
218+
let method =
219+
core::str::from_utf8(dtb.get_property("/psci", "method").unwrap_or(b"unknown"))
220+
.unwrap()
221+
.replace('\0', "");
222+
223+
let ttbr0: *mut u8;
224+
unsafe {
225+
asm!(
226+
"mrs {}, ttbr0_el1",
227+
out(reg) ttbr0,
228+
);
229+
}
230+
TTBR0.store(ttbr0, Ordering::Relaxed);
231+
232+
for cpu_id in 1..get_possible_cpus() {
233+
debug!("Try to wake-up core {cpu_id}");
234+
235+
if method == "hvc" {
236+
// call hypervisor to wakeup next core
237+
unsafe {
238+
asm!("hvc #0", in("x0") cpu_on, in("x1") cpu_id, in("x2") phys_start.as_u64(), in("x3") cpu_id, options(nomem, nostack));
239+
}
240+
} else if method == "smc" {
241+
// call secure monitor to wakeup next core
242+
unsafe {
243+
asm!("smc #0", in("x0") cpu_on, in("x1") cpu_id, in("x2") phys_start.as_u64(), in("x3") cpu_id, options(nomem, nostack));
244+
}
245+
} else {
246+
warn!("Method {method} isn't supported!");
247+
return;
248+
}
249+
250+
// wait for next core
251+
while CPU_ONLINE.0.load(Ordering::Relaxed) < cpu_id + 1 {
252+
spin_loop();
253+
}
254+
}
255+
}
256+
}
181257
}
182258

183259
pub fn print_statistics() {

src/arch/aarch64/kernel/processor.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,6 @@ pub fn seed_entropy() -> Option<[u8; 32]> {
9898
None
9999
}
100100

101-
pub(crate) fn run_on_hypervisor() -> bool {
102-
true
103-
}
104-
105101
/// The halt function stops the processor until the next interrupt arrives
106102
pub fn halt() {
107103
unsafe {
@@ -246,8 +242,5 @@ pub fn print_information() {
246242
infoheader!(" CPU INFORMATION ");
247243
infoentry!("Processor compatibility", str::from_utf8(reg).unwrap());
248244
infoentry!("Counter frequency", *CPU_FREQUENCY);
249-
if run_on_hypervisor() {
250-
info!("Run on hypervisor");
251-
}
252245
infofooter!();
253246
}

0 commit comments

Comments
 (0)