1#![no_std]
8
9use core::fmt::Write;
10
11use kernel::utilities::registers::interfaces::{Readable, Writeable};
12
13pub mod csr;
14pub mod pmp;
15pub mod support;
16pub mod syscall;
17pub mod thread_id;
18
19pub const XLEN: usize = if cfg!(target_arch = "riscv64") {
22    64
23} else {
24    32
25};
26
27extern "C" {
28    static _estack: usize;
31    static _sstack: usize;
32
33    static mut _szero: usize;
35    static mut _ezero: usize;
36
37    static mut _etext: usize;
39
40    static mut _srelocate: usize;
42    static mut _erelocate: usize;
43
44    #[link_name = "__global_pointer$"]
46    static __global_pointer: usize;
47}
48
49#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
60#[link_section = ".riscv.start"]
61#[unsafe(naked)]
62pub extern "C" fn _start() {
63    use core::arch::naked_asm;
64    naked_asm!(
65        "
66    // Set the global pointer register using the variable defined in the
67    // linker script. This register is only set once. The global pointer
68    // is a method for sharing state between the linker and the CPU so
69    // that the linker can emit code with offsets that are relative to
70    // the gp register, and the CPU can successfully execute them.
71    //
72    // https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
73    // https://groups.google.com/a/groups.riscv.org/forum/#!msg/sw-dev/60IdaZj27dY/5MydPLnHAQAJ
74    // https://www.sifive.com/blog/2017/08/28/all-aboard-part-3-linker-relaxation-in-riscv-toolchain/
75    //
76    // Disable linker relaxation for code that sets up GP so that this doesn't
77    // get turned into `mv gp, gp`.
78    .option push
79    .option norelax
80
81    la gp, {gp}                 // Set the global pointer from linker script.
82
83    // Re-enable linker relaxations.
84    .option pop
85
86    // Initialize the stack pointer register. This comes directly from
87    // the linker script.
88    la sp, {estack}             // Set the initial stack pointer.
89
90    // Set s0 (the frame pointer) to the start of the stack.
91    add  s0, sp, zero           // s0 = sp
92
93    // Initialize mscratch to 0 so that we know that we are currently
94    // in the kernel. This is used for the check in the trap handler.
95    csrw 0x340, zero            // CSR=0x340=mscratch
96
97    // INITIALIZE MEMORY
98
99    // Start by initializing .bss memory. The Tock linker script defines
100    // `_szero` and `_ezero` to mark the .bss segment.
101    la a0, {sbss}               // a0 = first address of .bss
102    la a1, {ebss}               // a1 = first address after .bss
103
104100: // bss_init_loop
105    beq  a0, a1, 101f           // If a0 == a1, we are done.
106    sw   zero, 0(a0)            // *a0 = 0. Write 0 to the memory location in a0.
107    addi a0, a0, 4              // a0 = a0 + 4. Increment pointer to next word.
108    j 100b                      // Continue the loop.
109
110101: // bss_init_done
111
112    // Now initialize .data memory. This involves coping the values right at the
113    // end of the .text section (in flash) into the .data section (in RAM).
114    la a0, {sdata}              // a0 = first address of data section in RAM
115    la a1, {edata}              // a1 = first address after data section in RAM
116    la a2, {etext}              // a2 = address of stored data initial values
117
118200: // data_init_loop
119    beq  a0, a1, 201f           // If we have reached the end of the .data
120                                // section then we are done.
121    lw   a3, 0(a2)              // a3 = *a2. Load value from initial values into a3.
122    sw   a3, 0(a0)              // *a0 = a3. Store initial value into
123                                // next place in .data.
124    addi a0, a0, 4              // a0 = a0 + 4. Increment to next word in memory.
125    addi a2, a2, 4              // a2 = a2 + 4. Increment to next word in flash.
126    j 200b                      // Continue the loop.
127
128201: // data_init_done
129
130    // With that initial setup out of the way, we now branch to the main
131    // code, likely defined in a board's main.rs.
132    j main
133        ",
134        gp = sym __global_pointer,
135        estack = sym _estack,
136        sbss = sym _szero,
137        ebss = sym _ezero,
138        sdata = sym _srelocate,
139        edata = sym _erelocate,
140        etext = sym _etext,
141    );
142}
143
144#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
146pub extern "C" fn _start() {
147    unimplemented!()
148}
149
150pub enum PermissionMode {
152    User = 0x0,
153    Supervisor = 0x1,
154    Reserved = 0x2,
155    Machine = 0x3,
156}
157
158pub unsafe fn configure_trap_handler() {
166    csr::CSR.mscratch.set(0);
168
169    csr::CSR.mtvec.write(
173        csr::mtvec::mtvec::trap_addr.val(_start_trap as usize >> 2)
174            + csr::mtvec::mtvec::mode::CLEAR,
175    );
176}
177
178#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
180pub extern "C" fn _start_trap() {
181    unimplemented!()
182}
183
184#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
279#[link_section = ".riscv.trap"]
280#[export_name = "_start_trap"]
286#[unsafe(naked)]
287pub extern "C" fn _start_trap() {
288    use core::arch::naked_asm;
289    naked_asm!(
290        "
291    // This is the global trap handler. By default, Tock expects this
292    // trap handler to be registered at all times, and that all traps
293    // and interrupts occurring in all modes of execution (M-, S-, and
294    // U-mode) will cause this trap handler to be executed.
295    //
296    // For documentation of its behavior, and how process
297    // implementations can hook their own trap handler code, see the
298    // comment on the `extern C _start_trap` symbol above.
299
300    // Atomically swap s0 and mscratch:
301    csrrw s0, mscratch, s0        // s0 = mscratch; mscratch = s0
302
303    // If mscratch contained 0, invoke the kernel trap handler.
304    beq   s0, x0, 100f      // if s0==x0: goto 100
305
306    // Else, save the current value of s1 to `0*4(s0)`, load `1*4(s0)`
307    // into s1 and jump to it (invoking a custom trap handler).
308    sw    s1, 0*4(s0)       // *s0 = s1
309    lw    s1, 1*4(s0)       // s1 = *(s0+4)
310    jr    s1                // goto s1
311
312  100: // _start_kernel_trap
313
314    // The global trap handler has swapped s0 into mscratch. We can thus
315    // freely clobber s0 without losing any information.
316    //
317    // Since we want to use the stack to save kernel registers, we
318    // first need to make sure that the trap wasn't the result of a
319    // stack overflow, in which case we can't use the current stack
320    // pointer. Use s0 as a scratch register:
321
322    // Load the address of the bottom of the stack (`_sstack`) into our
323    // newly freed-up s0 register.
324    la s0, {sstack}                     // s0 = _sstack
325
326    // Compare the kernel stack pointer to the bottom of the stack. If
327    // the stack pointer is above the bottom of the stack, then continue
328    // handling the fault as normal.
329    bgtu sp, s0, 200f                   // branch if sp > s0
330
331    // If we get here, then we did encounter a stack overflow. We are
332    // going to panic at this point, but for that to work we need a
333    // valid stack to run the panic code. We do this by just starting
334    // over with the kernel stack and placing the stack pointer at the
335    // top of the original stack.
336    la sp, {estack}                     // sp = _estack
337
338200: // _start_kernel_trap_continue
339
340    // Restore s0. We reset mscratch to 0 (kernel trap handler mode)
341    csrrw s0, mscratch, zero    // s0 = mscratch; mscratch = 0
342
343    // Make room for the caller saved registers we need to restore after running
344    // any trap handler code.
345    addi sp, sp, -20*4
346
347    // Save all of the caller saved registers.
348    sw   ra, 0*4(sp)
349    sw   t0, 1*4(sp)
350    sw   t1, 2*4(sp)
351    sw   t2, 3*4(sp)
352    sw   t3, 4*4(sp)
353    sw   t4, 5*4(sp)
354    sw   t5, 6*4(sp)
355    sw   t6, 7*4(sp)
356    sw   a0, 8*4(sp)
357    sw   a1, 9*4(sp)
358    sw   a2, 10*4(sp)
359    sw   a3, 11*4(sp)
360    sw   a4, 12*4(sp)
361    sw   a5, 13*4(sp)
362    sw   a6, 14*4(sp)
363    sw   a7, 15*4(sp)
364
365    // Save one callee-saved register (s0), which we place the address of
366    // the hart-specific 'are we in a trap handler' flag in:
367    sw   s0, 16*4(sp)
368
369    // Determine the address of the hart-specific 'are we in a trap handler'
370    // flag as an offset to the _trap_handler_active symbol. The chip crate
371    // is responsible for defining this symbol, and ensuring it is large
372    // enough to fit `max(mhartid) * MXLEN` bytes.
373    la   s0, _trap_handler_active // s0 = addr(_trap_handler_active)
374    csrr t0, mhartid              // t0 = hartid
375    slli t0, t0, 2                // t0 = t0 * 4
376    add  s0, s0, t0               // s0 = addr(_trap_handler_active[hartid])
377
378    // Indicate that we are in a trap handler on this hart:
379    li   t0, 1
380    sw   t0, 0(s0)
381
382    // Jump to board-specific trap handler code. Likely this was an
383    // interrupt and we want to disable a particular interrupt, but each
384    // board/chip can customize this as needed.
385    jal ra, _start_trap_rust_from_kernel
386
387    // Indicate that we are no longer going to be in a trap handler on this
388    // hart:
389    sw   x0, 0(s0)
390
391    // Restore the caller saved registers from the stack.
392    lw   ra, 0*4(sp)
393    lw   t0, 1*4(sp)
394    lw   t1, 2*4(sp)
395    lw   t2, 3*4(sp)
396    lw   t3, 4*4(sp)
397    lw   t4, 5*4(sp)
398    lw   t5, 6*4(sp)
399    lw   t6, 7*4(sp)
400    lw   a0, 8*4(sp)
401    lw   a1, 9*4(sp)
402    lw   a2, 10*4(sp)
403    lw   a3, 11*4(sp)
404    lw   a4, 12*4(sp)
405    lw   a5, 13*4(sp)
406    lw   a6, 14*4(sp)
407    lw   a7, 15*4(sp)
408
409    // Restore the one callee-saved register (s0), which used to hold the
410    // address of the hart-specific 'are we in a trap handler flag':
411    lw   s0, 16*4(sp)
412
413    // Reset the stack pointer.
414    addi sp, sp, 20*4
415
416    // mret returns from the trap handler. The PC is set to what is in
417    // mepc and execution proceeds from there. Since we did not modify
418    // mepc we will return to where the exception occurred.
419    mret
420        ",
421        estack = sym _estack,
422        sstack = sym _sstack,
423    );
424}
425
426#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
438pub unsafe fn semihost_command(command: usize, arg0: usize, arg1: usize) -> usize {
439    use core::arch::asm;
440    let res;
441    asm!(
442        "
443    .balign 16
444    .option push
445    .option norelax
446    .option norvc
447    slli x0, x0, 0x1f
448    ebreak
449    srai x0, x0, 7
450    .option pop
451        ",
452        in("a0") command,
453        in("a1") arg0,
454        in("a2") arg1,
455        lateout("a0") res,
456    );
457    res
458}
459
460#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
462pub unsafe fn semihost_command(_command: usize, _arg0: usize, _arg1: usize) -> usize {
463    unimplemented!()
464}
465
466pub unsafe fn print_mcause(mcval: csr::mcause::Trap, writer: &mut dyn Write) {
468    match mcval {
469        csr::mcause::Trap::Interrupt(interrupt) => match interrupt {
470            csr::mcause::Interrupt::UserSoft => {
471                let _ = writer.write_fmt(format_args!("User software interrupt"));
472            }
473            csr::mcause::Interrupt::SupervisorSoft => {
474                let _ = writer.write_fmt(format_args!("Supervisor software interrupt"));
475            }
476            csr::mcause::Interrupt::MachineSoft => {
477                let _ = writer.write_fmt(format_args!("Machine software interrupt"));
478            }
479            csr::mcause::Interrupt::UserTimer => {
480                let _ = writer.write_fmt(format_args!("User timer interrupt"));
481            }
482            csr::mcause::Interrupt::SupervisorTimer => {
483                let _ = writer.write_fmt(format_args!("Supervisor timer interrupt"));
484            }
485            csr::mcause::Interrupt::MachineTimer => {
486                let _ = writer.write_fmt(format_args!("Machine timer interrupt"));
487            }
488            csr::mcause::Interrupt::UserExternal => {
489                let _ = writer.write_fmt(format_args!("User external interrupt"));
490            }
491            csr::mcause::Interrupt::SupervisorExternal => {
492                let _ = writer.write_fmt(format_args!("Supervisor external interrupt"));
493            }
494            csr::mcause::Interrupt::MachineExternal => {
495                let _ = writer.write_fmt(format_args!("Machine external interrupt"));
496            }
497            csr::mcause::Interrupt::Unknown(_) => {
498                let _ = writer.write_fmt(format_args!("Reserved/Unknown"));
499            }
500        },
501        csr::mcause::Trap::Exception(exception) => match exception {
502            csr::mcause::Exception::InstructionMisaligned => {
503                let _ = writer.write_fmt(format_args!("Instruction access misaligned"));
504            }
505            csr::mcause::Exception::InstructionFault => {
506                let _ = writer.write_fmt(format_args!("Instruction access fault"));
507            }
508            csr::mcause::Exception::IllegalInstruction => {
509                let _ = writer.write_fmt(format_args!("Illegal instruction"));
510            }
511            csr::mcause::Exception::Breakpoint => {
512                let _ = writer.write_fmt(format_args!("Breakpoint"));
513            }
514            csr::mcause::Exception::LoadMisaligned => {
515                let _ = writer.write_fmt(format_args!("Load address misaligned"));
516            }
517            csr::mcause::Exception::LoadFault => {
518                let _ = writer.write_fmt(format_args!("Load access fault"));
519            }
520            csr::mcause::Exception::StoreMisaligned => {
521                let _ = writer.write_fmt(format_args!("Store/AMO address misaligned"));
522            }
523            csr::mcause::Exception::StoreFault => {
524                let _ = writer.write_fmt(format_args!("Store/AMO access fault"));
525            }
526            csr::mcause::Exception::UserEnvCall => {
527                let _ = writer.write_fmt(format_args!("Environment call from U-mode"));
528            }
529            csr::mcause::Exception::SupervisorEnvCall => {
530                let _ = writer.write_fmt(format_args!("Environment call from S-mode"));
531            }
532            csr::mcause::Exception::MachineEnvCall => {
533                let _ = writer.write_fmt(format_args!("Environment call from M-mode"));
534            }
535            csr::mcause::Exception::InstructionPageFault => {
536                let _ = writer.write_fmt(format_args!("Instruction page fault"));
537            }
538            csr::mcause::Exception::LoadPageFault => {
539                let _ = writer.write_fmt(format_args!("Load page fault"));
540            }
541            csr::mcause::Exception::StorePageFault => {
542                let _ = writer.write_fmt(format_args!("Store/AMO page fault"));
543            }
544            csr::mcause::Exception::Unknown => {
545                let _ = writer.write_fmt(format_args!("Reserved"));
546            }
547        },
548    }
549}
550
551pub unsafe fn print_riscv_state(writer: &mut dyn Write) {
554    let mcval: csr::mcause::Trap = core::convert::From::from(csr::CSR.mcause.extract());
555    let _ = writer.write_fmt(format_args!("\r\n---| RISC-V Machine State |---\r\n"));
556    let _ = writer.write_fmt(format_args!("Last cause (mcause): "));
557    print_mcause(mcval, writer);
558    let interrupt = csr::CSR.mcause.read(csr::mcause::mcause::is_interrupt);
559    let code = csr::CSR.mcause.read(csr::mcause::mcause::reason);
560    let _ = writer.write_fmt(format_args!(
561        " (interrupt={}, exception code={:#010X})",
562        interrupt, code
563    ));
564    let _ = writer.write_fmt(format_args!(
565        "\r\nLast value (mtval):  {:#010X}\
566         \r\n\
567         \r\nSystem register dump:\
568         \r\n mepc:    {:#010X}    mstatus:     {:#010X}\
569         \r\n mcycle:  {:#010X}    minstret:    {:#010X}\
570         \r\n mtvec:   {:#010X}",
571        csr::CSR.mtval.get(),
572        csr::CSR.mepc.get(),
573        csr::CSR.mstatus.get(),
574        csr::CSR.mcycle.get(),
575        csr::CSR.minstret.get(),
576        csr::CSR.mtvec.get()
577    ));
578    let mstatus = csr::CSR.mstatus.extract();
579    let uie = mstatus.is_set(csr::mstatus::mstatus::uie);
580    let sie = mstatus.is_set(csr::mstatus::mstatus::sie);
581    let mie = mstatus.is_set(csr::mstatus::mstatus::mie);
582    let upie = mstatus.is_set(csr::mstatus::mstatus::upie);
583    let spie = mstatus.is_set(csr::mstatus::mstatus::spie);
584    let mpie = mstatus.is_set(csr::mstatus::mstatus::mpie);
585    let spp = mstatus.is_set(csr::mstatus::mstatus::spp);
586    let _ = writer.write_fmt(format_args!(
587        "\r\n mstatus: {:#010X}\
588         \r\n  uie:    {:5}  upie:   {}\
589         \r\n  sie:    {:5}  spie:   {}\
590         \r\n  mie:    {:5}  mpie:   {}\
591         \r\n  spp:    {}",
592        mstatus.get(),
593        uie,
594        upie,
595        sie,
596        spie,
597        mie,
598        mpie,
599        spp
600    ));
601    let e_usoft = csr::CSR.mie.is_set(csr::mie::mie::usoft);
602    let e_ssoft = csr::CSR.mie.is_set(csr::mie::mie::ssoft);
603    let e_msoft = csr::CSR.mie.is_set(csr::mie::mie::msoft);
604    let e_utimer = csr::CSR.mie.is_set(csr::mie::mie::utimer);
605    let e_stimer = csr::CSR.mie.is_set(csr::mie::mie::stimer);
606    let e_mtimer = csr::CSR.mie.is_set(csr::mie::mie::mtimer);
607    let e_uext = csr::CSR.mie.is_set(csr::mie::mie::uext);
608    let e_sext = csr::CSR.mie.is_set(csr::mie::mie::sext);
609    let e_mext = csr::CSR.mie.is_set(csr::mie::mie::mext);
610
611    let p_usoft = csr::CSR.mip.is_set(csr::mip::mip::usoft);
612    let p_ssoft = csr::CSR.mip.is_set(csr::mip::mip::ssoft);
613    let p_msoft = csr::CSR.mip.is_set(csr::mip::mip::msoft);
614    let p_utimer = csr::CSR.mip.is_set(csr::mip::mip::utimer);
615    let p_stimer = csr::CSR.mip.is_set(csr::mip::mip::stimer);
616    let p_mtimer = csr::CSR.mip.is_set(csr::mip::mip::mtimer);
617    let p_uext = csr::CSR.mip.is_set(csr::mip::mip::uext);
618    let p_sext = csr::CSR.mip.is_set(csr::mip::mip::sext);
619    let p_mext = csr::CSR.mip.is_set(csr::mip::mip::mext);
620    let _ = writer.write_fmt(format_args!(
621        "\r\n mie:   {:#010X}   mip:   {:#010X}\
622         \r\n  usoft:  {:6}              {:6}\
623         \r\n  ssoft:  {:6}              {:6}\
624         \r\n  msoft:  {:6}              {:6}\
625         \r\n  utimer: {:6}              {:6}\
626         \r\n  stimer: {:6}              {:6}\
627         \r\n  mtimer: {:6}              {:6}\
628         \r\n  uext:   {:6}              {:6}\
629         \r\n  sext:   {:6}              {:6}\
630         \r\n  mext:   {:6}              {:6}\r\n",
631        csr::CSR.mie.get(),
632        csr::CSR.mip.get(),
633        e_usoft,
634        p_usoft,
635        e_ssoft,
636        p_ssoft,
637        e_msoft,
638        p_msoft,
639        e_utimer,
640        p_utimer,
641        e_stimer,
642        p_stimer,
643        e_mtimer,
644        p_mtimer,
645        e_uext,
646        p_uext,
647        e_sext,
648        p_sext,
649        e_mext,
650        p_mext
651    ));
652}