@@ -6,7 +6,7 @@ use core::sync::atomic::{AtomicU64, Ordering};
6
6
7
7
use aarch64:: regs:: * ;
8
8
use ahash:: RandomState ;
9
- use arm_gic:: gicv3:: GicV3 ;
9
+ use arm_gic:: gicv3:: { GicV3 , SgiTarget } ;
10
10
use arm_gic:: { IntId , Trigger } ;
11
11
use hashbrown:: HashMap ;
12
12
use hermit_dtb:: Dtb ;
@@ -55,10 +55,7 @@ pub fn enable() {
55
55
}
56
56
}
57
57
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)
62
59
#[ inline]
63
60
pub fn enable_and_wait ( ) {
64
61
unsafe {
@@ -94,13 +91,8 @@ pub(crate) fn install_handlers() {
94
91
debug ! ( "Handle timer interrupt" ) ;
95
92
96
93
// 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 ) ;
104
96
}
105
97
106
98
for ( key, value) in get_interrupt_handlers ( ) . into_iter ( ) {
@@ -237,8 +229,19 @@ pub(crate) extern "C" fn do_error(_state: &State) -> ! {
237
229
scheduler:: abort ( )
238
230
}
239
231
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
+ ) ;
242
245
}
243
246
244
247
pub ( crate ) fn init ( ) {
@@ -261,21 +264,31 @@ pub(crate) fn init() {
261
264
let ( slice, _residual_slice) = residual_slice. split_at ( core:: mem:: size_of :: < u64 > ( ) ) ;
262
265
let gicr_size = u64:: from_be_bytes ( slice. try_into ( ) . unwrap ( ) ) ;
263
266
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
-
269
267
let num_cpus = dtb
270
268
. enum_subnodes ( "/cpus" )
271
269
. filter ( |name| name. contains ( "cpu@" ) )
272
270
. 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
+ } ;
274
288
275
- info ! ( "Found {num_cpus} cpus!" ) ;
276
289
info ! ( "Found GIC Distributor interface at {gicd_start:p} (size {gicd_size:#X})" ) ;
277
290
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})"
279
292
) ;
280
293
281
294
let gicd_address =
@@ -301,16 +314,16 @@ pub(crate) fn init() {
301
314
flags,
302
315
) ;
303
316
304
- GicV3 :: set_priority_mask ( 0xff ) ;
305
317
let mut gic = unsafe {
306
318
GicV3 :: new (
307
319
gicd_address. as_mut_ptr ( ) ,
308
320
gicr_address. as_mut_ptr ( ) ,
309
321
num_cpus,
310
- gicr_stride as usize ,
322
+ is_gic_v4 ,
311
323
)
312
324
} ;
313
- gic. setup ( cpu_id as usize ) ;
325
+ gic. setup ( cpu_id) ;
326
+ GicV3 :: set_priority_mask ( 0xff ) ;
314
327
315
328
for node in dtb. enum_subnodes ( "/" ) {
316
329
let parts: Vec < _ > = node. split ( '@' ) . collect ( ) ;
@@ -349,27 +362,91 @@ pub(crate) fn init() {
349
362
} else {
350
363
panic ! ( "Invalid interrupt type" ) ;
351
364
} ;
352
- gic. set_interrupt_priority ( timer_irqid, Some ( cpu_id as usize ) , 0x00 ) ;
365
+ gic. set_interrupt_priority ( timer_irqid, Some ( cpu_id) , 0x00 ) ;
353
366
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 ) ;
355
368
} 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 ) ;
357
370
} else {
358
371
panic ! ( "Invalid interrupt level!" ) ;
359
372
}
360
- gic. enable_interrupt ( timer_irqid, Some ( cpu_id as usize ) , true ) ;
373
+ gic. enable_interrupt ( timer_irqid, Some ( cpu_id) , true ) ;
361
374
}
362
375
}
363
376
}
364
377
365
378
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 ) ;
368
381
IRQ_NAMES . lock ( ) . insert ( SGI_RESCHED , "Reschedule" ) ;
369
382
370
383
* GIC . lock ( ) = Some ( gic) ;
371
384
}
372
385
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
+
373
450
static IRQ_NAMES : InterruptTicketMutex < HashMap < u8 , & ' static str , RandomState > > =
374
451
InterruptTicketMutex :: new ( HashMap :: with_hasher ( RandomState :: with_seeds ( 0 , 0 , 0 , 0 ) ) ) ;
375
452
0 commit comments