@@ -313,11 +313,122 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {
313
313
res. parse ( ) . unwrap ( )
314
314
}
315
315
316
+ #[ derive( Clone , Copy ) ]
316
317
enum RiscvArch {
317
318
Rv32 ,
318
319
Rv64 ,
319
320
}
320
321
322
+ const TRAP_SIZE : usize = 16 ;
323
+
324
+ #[ rustfmt:: skip]
325
+ const TRAP_FRAME : [ & str ; TRAP_SIZE ] = [
326
+ "ra" ,
327
+ "t0" ,
328
+ "t1" ,
329
+ "t2" ,
330
+ "t3" ,
331
+ "t4" ,
332
+ "t5" ,
333
+ "t6" ,
334
+ "a0" ,
335
+ "a1" ,
336
+ "a2" ,
337
+ "a3" ,
338
+ "a4" ,
339
+ "a5" ,
340
+ "a6" ,
341
+ "a7" ,
342
+ ] ;
343
+
344
+ fn store_trap < T : FnMut ( & str ) -> bool > ( arch : RiscvArch , mut filter : T ) -> String {
345
+ let ( width, store) = match arch {
346
+ RiscvArch :: Rv32 => ( 4 , "sw" ) ,
347
+ RiscvArch :: Rv64 => ( 8 , "sd" ) ,
348
+ } ;
349
+ let mut stores = Vec :: new ( ) ;
350
+ for ( i, reg) in TRAP_FRAME
351
+ . iter ( )
352
+ . enumerate ( )
353
+ . filter ( |( _, & reg) | filter ( reg) )
354
+ {
355
+ stores. push ( format ! ( "{store} {reg}, {i}*{width}(sp)" ) ) ;
356
+ }
357
+ stores. join ( "\n " )
358
+ }
359
+
360
+ fn load_trap ( arch : RiscvArch ) -> String {
361
+ let ( width, load) = match arch {
362
+ RiscvArch :: Rv32 => ( 4 , "lw" ) ,
363
+ RiscvArch :: Rv64 => ( 8 , "ld" ) ,
364
+ } ;
365
+ let mut loads = Vec :: new ( ) ;
366
+ for ( i, reg) in TRAP_FRAME . iter ( ) . enumerate ( ) {
367
+ loads. push ( format ! ( "{load} {reg}, {i}*{width}(sp)" ) ) ;
368
+ }
369
+ loads. join ( "\n " )
370
+ }
371
+
372
+ #[ proc_macro]
373
+ pub fn weak_start_trap_riscv32 ( _input : TokenStream ) -> TokenStream {
374
+ weak_start_trap ( RiscvArch :: Rv32 )
375
+ }
376
+
377
+ #[ proc_macro]
378
+ pub fn weak_start_trap_riscv64 ( _input : TokenStream ) -> TokenStream {
379
+ weak_start_trap ( RiscvArch :: Rv64 )
380
+ }
381
+
382
+ fn weak_start_trap ( arch : RiscvArch ) -> TokenStream {
383
+ let width = match arch {
384
+ RiscvArch :: Rv32 => 4 ,
385
+ RiscvArch :: Rv64 => 8 ,
386
+ } ;
387
+ // ensure we do not break that sp is 16-byte aligned
388
+ if ( TRAP_SIZE * width) % 16 != 0 {
389
+ return parse:: Error :: new ( Span :: call_site ( ) , "Trap frame size must be 16-byte aligned" )
390
+ . to_compile_error ( )
391
+ . into ( ) ;
392
+ }
393
+ let store = store_trap ( arch, |_| true ) ;
394
+ let load = load_trap ( arch) ;
395
+
396
+ #[ cfg( feature = "s-mode" ) ]
397
+ let ret = "sret" ;
398
+ #[ cfg( not( feature = "s-mode" ) ) ]
399
+ let ret = "mret" ;
400
+
401
+ let instructions: proc_macro2:: TokenStream = format ! (
402
+ "
403
+ core::arch::global_asm!(
404
+ \" .section .trap, \\ \" ax\\ \"
405
+ .align {width}
406
+ .weak _start_trap
407
+ _start_trap:
408
+ addi sp, sp, - {TRAP_SIZE} * {width}
409
+ {store}
410
+ add a0, sp, zero
411
+ jal ra, _start_trap_rust
412
+ {load}
413
+ addi sp, sp, {TRAP_SIZE} * {width}
414
+ {ret}
415
+ \" );"
416
+ )
417
+ . parse ( )
418
+ . unwrap ( ) ;
419
+
420
+ #[ cfg( feature = "v-trap" ) ]
421
+ let v_trap = v_trap:: continue_interrupt_trap ( arch) ;
422
+ #[ cfg( not( feature = "v-trap" ) ) ]
423
+ let v_trap = proc_macro2:: TokenStream :: new ( ) ;
424
+
425
+ quote ! (
426
+ #instructions
427
+ #v_trap
428
+ )
429
+ . into ( )
430
+ }
431
+
321
432
#[ proc_macro_attribute]
322
433
pub fn interrupt_riscv32 ( args : TokenStream , input : TokenStream ) -> TokenStream {
323
434
interrupt ( args, input, RiscvArch :: Rv32 )
@@ -376,7 +487,7 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
376
487
#[ cfg( not( feature = "v-trap" ) ) ]
377
488
let start_trap = proc_macro2:: TokenStream :: new ( ) ;
378
489
#[ cfg( feature = "v-trap" ) ]
379
- let start_trap = v_trap:: start_interrupt_trap_asm ( ident, _arch) ;
490
+ let start_trap = v_trap:: start_interrupt_trap ( ident, _arch) ;
380
491
381
492
quote ! (
382
493
#start_trap
@@ -390,45 +501,41 @@ fn interrupt(args: TokenStream, input: TokenStream, _arch: RiscvArch) -> TokenSt
390
501
mod v_trap {
391
502
use super :: * ;
392
503
393
- const TRAP_SIZE : usize = 16 ;
394
-
395
- #[ rustfmt:: skip]
396
- const TRAP_FRAME : [ & str ; TRAP_SIZE ] = [
397
- "ra" ,
398
- "t0" ,
399
- "t1" ,
400
- "t2" ,
401
- "t3" ,
402
- "t4" ,
403
- "t5" ,
404
- "t6" ,
405
- "a0" ,
406
- "a1" ,
407
- "a2" ,
408
- "a3" ,
409
- "a4" ,
410
- "a5" ,
411
- "a6" ,
412
- "a7" ,
413
- ] ;
414
-
415
- pub ( crate ) fn start_interrupt_trap_asm (
504
+ pub ( crate ) fn start_interrupt_trap (
416
505
ident : & syn:: Ident ,
417
506
arch : RiscvArch ,
418
507
) -> proc_macro2:: TokenStream {
419
- let function = ident. to_string ( ) ;
420
- let ( width, store , load ) = match arch {
421
- RiscvArch :: Rv32 => ( 4 , "sw" , "lw" ) ,
422
- RiscvArch :: Rv64 => ( 8 , "sd" , "ld" ) ,
508
+ let interrupt = ident. to_string ( ) ;
509
+ let width = match arch {
510
+ RiscvArch :: Rv32 => 4 ,
511
+ RiscvArch :: Rv64 => 8 ,
423
512
} ;
513
+ let store = store_trap ( arch, |r| r == "a0" ) ;
424
514
425
- let ( mut stores, mut loads) = ( Vec :: new ( ) , Vec :: new ( ) ) ;
426
- for ( i, r) in TRAP_FRAME . iter ( ) . enumerate ( ) {
427
- stores. push ( format ! ( " {store} {r}, {i}*{width}(sp)" ) ) ;
428
- loads. push ( format ! ( " {load} {r}, {i}*{width}(sp)" ) ) ;
429
- }
430
- let store = stores. join ( "\n " ) ;
431
- let load = loads. join ( "\n " ) ;
515
+ let instructions = format ! (
516
+ "
517
+ core::arch::global_asm!(
518
+ \" .section .trap, \\ \" ax\\ \"
519
+ .align {width}
520
+ .global _start_{interrupt}_trap
521
+ _start_{interrupt}_trap:
522
+ addi sp, sp, -{TRAP_SIZE} * {width} // allocate space for trap frame
523
+ {store} // store trap partially (only register a0)
524
+ la a0, {interrupt} // load interrupt handler address into a0
525
+ j _continue_interrupt_trap // jump to common part of interrupt trap
526
+ \" );"
527
+ ) ;
528
+
529
+ instructions. parse ( ) . unwrap ( )
530
+ }
531
+
532
+ pub ( crate ) fn continue_interrupt_trap ( arch : RiscvArch ) -> proc_macro2:: TokenStream {
533
+ let width = match arch {
534
+ RiscvArch :: Rv32 => 4 ,
535
+ RiscvArch :: Rv64 => 8 ,
536
+ } ;
537
+ let store = store_trap ( arch, |reg| reg != "a0" ) ;
538
+ let load = load_trap ( arch) ;
432
539
433
540
#[ cfg( feature = "s-mode" ) ]
434
541
let ret = "sret" ;
@@ -439,16 +546,15 @@ mod v_trap {
439
546
"
440
547
core::arch::global_asm!(
441
548
\" .section .trap, \\ \" ax\\ \"
442
- .align {width}
443
- .global _start_{function}_trap
444
- _start_{function}_trap:
445
- addi sp, sp, - {TRAP_SIZE} * {width}
446
- {store}
447
- call {function}
448
- {load}
449
- addi sp, sp, {TRAP_SIZE} * {width}
450
- {ret}\"
451
- );"
549
+ .align {width} // TODO is this necessary?
550
+ .global _continue_interrupt_trap
551
+ _continue_interrupt_trap:
552
+ {store} // store trap partially (all registers except a0)
553
+ jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0)
554
+ {load} // restore trap frame
555
+ addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for trap frame
556
+ {ret} // return from interrupt
557
+ \" );"
452
558
) ;
453
559
454
560
instructions. parse ( ) . unwrap ( )
0 commit comments